Android 之 BroadcastReceiver

概念

作为Android的四大主件之一,这种组件本质上是一种全局的监听器,用于监听系统全局的广播消息,以及在应用程序之间进行信息的传输。
例如:
当电池电量低、系统的时间变化、系统收到短信等等,这些状态发生时系统会对外发送标准广播,我们便可以通过继承BroadcastReceiver来创建自己的广播接收器监听这些标准广播,但广播到达时执行自己的操作。

使用场景

Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器)。广播作为Android组件间的通信方式,可以使用的场景如下:
1.同一app内部的同一组件内的消息通信(单个或多个线程之间);----无意义 可以采用Handler 没必要
2.同一app内部的不同组件之间的消息通信(单个进程);————应用场景较少。
3.同一app具有多个进程的不同组件之间的消息通信;
4.不同app之间的组件之间消息通信;
5.Android系统在特定情况下与App之间的消息通信。

其中3、4、5 需要对不同进程间的消息通信,此时根据实际业务使用广播机制会显得非常适宜。

Android中的广播使用了观察者模式,基于消息的发布/订阅事件模型。因此,从实现的角度来看,Android中的广播将广播的发送者和接受者极大程度上解耦,使得系统能够方便集成,更易扩展。

一、广播创建

广播创建不要太简单,作为四大主件之一,都有一共同的特点继承
此处我们写一个类继承BroadcastReceiver,并重写其抽象方法onReceive()

//自定义广播 
public class MyBroadCastReceiver extends BroadcastReceiver {

    //注册的广播行为,此处行为可以是任意字符串,只需要匹配我们广播注册时的行为就可以,行为可以是多个。
    //这里我写的是系统短信到达的行为
    public static final String INENT_ACTION="android.provider.Telephony.SMS_RECEIVED";

    @Override
    public void onReceive(Context context, Intent intent) {
        //通过代码 匹配 广播发送时的行为,当行为匹配,执行我们的操作
        if(intent.getAction().equals(INENT_ACTION)){
            Log.e(TAG,"匹配到我们的意图");
        }

    }
}
二、广播注册

想要监听广播,必然需要注册属于我们的广播接收器
这里我们有两种注册方式:

1种: 代码中动态注册
2种: 在Manifest.xml中静态注册

注册方式不同,当然是有所区别的

非常驻型:
当你使用第一种方式注册的时候,该广播接收器不是常驻类型,也就是说广播的接收器会跟随主件,程序的生命周期销毁而销毁,一般我们会自己手动进行解除注册,避免内存的泄漏。

常驻型:
当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行(程序退出,进程未被杀死的情况下)。
通过几次测试,现在的一键杀死,杀死应用程序进程后无法被掉起,接着由于系统权限越来越🐂B,现在当监听到系统开机广播时也无法调起我们的App广播。除非你的应用程序被注册到系统应用程序内。

注册方式一:清单注册

  <receiver android:name=".MyBroadCastReceiver">
      <!-- android:priority属性是设置此接收者的优先级(从-1000到1000)
        用于有序广播,值越大 , 优先级越高
        同级别接收是先后随机的;级别低的收到广播。
      -->
      <intent-filter android:priority="1000">
              <!--注册的广播行为,此处是监听的系统短信到达-->
             <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>
 </receiver>

注册方式二:代码注册

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    MyBroadCastReceiver mRecevier;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /**
         * 代码注册广播,随程序的生命周期
         */
        mRecevier = new MyBroadCastReceiver();
        IntentFilter filter = new IntentFilter();
        //用于拦截短信的action
        filter.setPriority(1000);
        //行为定义,可以是多个
        filter.addAction("android.provider.Telephony.SMS_RECEIVED");
        //代码注册广播
        registerReceiver(mRecevier,filter);
    }

   @Override
    protected void onDestroy() {
        //解除广播注册
        unregisterReceiver(mRecevier);
        super.onDestroy();
    }
}
三、广播发送

广播的发送,分为两种类型:无序有序

//无序:直接传递一个意图
sendBroadcast(Intent intent)
//无序:传递一个意图和权限
sendBroadcast(Intent intent, String receiverPermission)
//有序:传递一个意图和权限
sendOrderedBroadcast(Intent intent,String receiverPermission) 
//有序:传递一个意图和权限附加额外信息
//resultReceiver:传入一个receiver作为此次有序广播最后一个执行的广播
//scheduler:指定要运行的广播接收器的线程。
//initialCode:初始码  通过getResultCode()可以获取
//initialData:初始数据  通过getResultData()可以获取
//initialExtras:初始附加功能  通过getResultExtras(true)可以获取
sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver,Handler scheduler, int initialCode, String initialData,Bundle initialExtras) 

代码演示:
无序,无权限

  Intent intent = new Intent();
  intent.setAction(MyBroadCastReceiver.LOAD_MSG);
  //发送无序广播
  sendBroadcast(intent);

无序,有权限

  Intent intent = new Intent();
  intent.setAction(MyBroadCastReceiver.LOAD_MSG);
  //发送无序带权限广播
  sendBroadcast(intent,"android.permission.RECEIVE_SMS");

有序,不带额外参数

 Intent intent = new Intent();
 intent.setAction(MyBroadCastReceiver.LOAD_TEL);
//有序广播:根据广播优先级 
//第二个参数 为 接收的广播 的 App所具备的权限 如果为NULL 则不需要权限
//如果传递了权限,只有在AndroidManifest.xml中要添加对应的权限的应用才能获取到这条广播。
sendOrderedBroadcast(intent,null);
sendOrderedBroadcast(intent,"android.permission.RECEIVE_SMS");

有序,带额外参数

Intent intent = new Intent();
intent.setAction(MyBroadCastReceiver.LOAD_TEL);

Bundle extras = new Bundle();
extras.putString("extras","其它数据");
//此处权限为null,线程为默认主线程
sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
   @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG,"最后一个被调用的receiver");
        int resultCode = getResultCode();   
        String resultData = getResultData();
        Bundle resultExtras = getResultExtras(true);
    }
 }, null, 10086, "初始数据", extras);

关于有序广播:
既然广播是一个有序的,那么我们就可以在中间拦截低级别的广播,并中止广播的传递。利用isOrderedBroadcast()判断是否是有序广播
利用abortBroadcast()拦截广播

public class MyBroadCastReceiver2 extends BroadcastReceiver {

    public static final String INENT_ACTION="android.provider.Telephony.SMS_RECEIVED";

    @Override
    public void onReceive(Context context, Intent intent) {
        //自定义广播 通过代码注册
        if(intent.getAction().equals(INENT_ACTION)){
            //判断是否是  有序广播
            boolean orderedBroadcast = isOrderedBroadcast();
            if (orderedBroadcast){
                //获取resultData
                String resultData = getResultData();
                //获取resultCode
                int resultCode = getResultCode();
                //获取resultExtras
                Bundle resultExtras = getResultExtras(true);
                if(resultExtras!=null){
                    //修改数据
                    resultExtras.putString("extras","你掉了10块钱");
                }
                //重新设置result值,那么下一个广播接受到的值就是我们修改过后的值.
                setResult(40,"我修改了",resultExtras);

                //如果不需要传递,可以使用abortBroadcast进行拦截
                abortBroadcast();
            }
        }
    }
}
四、权限问题

第一种场景: 谁有权收我的广播?
首先我们可以声明一个自定义权限
在Androidmanifest.xml中定义

<!--自定义权限:
normal:低风险权限,只要申请了就可以使用,安装时不需要用户确认 
dangerous:高风险权限,安装时需要用户的确认才可使用;
signature:只有当申请权限的应用程序的数字签名与声明此权限的应用程序的数字签名相同时 (如果是申请系统权限,则需要与系统签名相同),才能将权限授给它;
signatureOrSystem:签名相同,或者申请权限的应用为系统应用(在system image中)。
一般就normal了
-->
<permission 
    android:name = "com.android.permission.my_permission"
    android:protectionLevel="normal"  />  
<!-- 注册自定义的权限 -->
<uses-permission android:name="com.android.permission.my_permission" />

做完以上操作,发送广播时如果带有此权限,那么只有注册了此权限的应用才能接收这条广播。sendBroadcast(intent,"com.android.permission.my_permission");

第二种场景: 谁有权给我发广播?
当我们在清单文件中注册广播时可以加入权限,此处广播上添加了权限。
android:permission="com.android.permission.my_permission"

<receiver android:name=".MyReceiver" 
          android:permission="com.android.permission.my_permission">   
    <intent-filter>  
         <action android:name="com.android.MY_ACTION" />   
    </intent-filter>  
</receiver> 

这样一来,我们定义的照顾Receiver只能接收来自具有我们注册的权限的应用发出的广播了。需要进行以下注册

<!-- 注册自定义的权限 -->
<uses-permission android:name="com.android.permission.my_permission" />
五、本地广播

我们看出通过权限的控制,我们可以让自己的广播只被我们自己的应用接收,但是太多的权限设置和发布比较繁琐。
所以Google给我们提供了一枚专注于应用内的广播 LocalBroadcastReceiver(此处的App应用以App应用进程为界)

相比于全局广播,App应用内广播优势体现在:
1.安全性更高;
2.更加高效。

为此,Android v4兼容包中给出了封装好的LocalBroadcastManager类,用于统一处理App应用内的广播问题,
使用方式上与通常的全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将主调context变成了LocalBroadcastManager的单一实例。

Android本地广播机制:使用这个机制发出的广播只能够在应用程序的内部进行传递.
并且广播接收器也只能接收来自本应用程序发出的广播,这样所有的安全性问题就都不存在了。

本地广播是无法通过静态注册的方式来接收的。因为要用时程序肯定已经启动了。

注册与解除注册

/**
 * LocalBroadcastManager  应用内广播 只针对自身应用广播
 */
public class LocalReceiverActivity extends AppCompatActivity {

    private LocalBroadcastManager instance;
    private LocalReceiver localReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        //使用此方式注册的广播 只有本应用程序才可以广播到
        instance = LocalBroadcastManager.getInstance(this);
        localReceiver = new LocalReceiver();
        //意图过滤器
        IntentFilter filter = new IntentFilter();
        //添加行为
        filter.addAction(LocalReceiver.FLAG);
        instance.registerReceiver(localReceiver,filter);
    }

    @Override
    protected void onDestroy() {
        instance.unregisterReceiver(localReceiver);
        super.onDestroy();
    }
}

LocalReceiver 与我们之前的广播创建没区别都继承至BroadcastReceiver。

public class LocalReceiver extends BroadcastReceiver {

    public static final String FLAG = "localReceiver.com";
    private static final String TAG =FLAG;

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG,"应用内广播");
    }
}

至此广播就介绍完了,广播功能虽然挺强大,不过在实际中用得还是比较少,应用内的通信我个人更喜欢用EventBus

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容