Android--四大组件之BroadCastReceiver

作者: IT老五 | 来源:发表于2018-06-26 10:16 被阅读41次

    1. BroadCastReceiver是什么?

    2. 广播类型

    1). 有序广播
    2). 无序广播

    3. 生命周期

    4. 实现原理

    5. 使用方法

    6. 权限问题(安全性)

    7. LocalBroadcast

    8. 注意事项

    BroadCastReceiver

    BroadCastReceiver即广播接收器,用于监听/接收Android应用分发的各类广播并做出相应的响应。

    应用场景:

    1. 监听系统事件:如开机广播,网络连接与断开,屏幕开启与关闭等
    2. 不同组件间通信(多个Activity/service,包括不同应用间)
    3. 多线程通信

    广播类型

    广播可分为无序广播和有序广播:

    无序广播

    无序广播即广播被发送后,BroadCastReceiver之间是无顺序,完全异步的,各个Receiver之间无关联。无序广播无法通过abortBroadcast终止,也无法使用setResult和getResult来传递处理结果。
    无序广播直接通过Context.sendBroadcast()来发送。

    有序广播

    有序广播即广播发送后会按照优先级顺序被不同的广播接收器接收,优先级可以通过intent-filter的android:priority属性来设置,定义范围为-1000~1000,数值越大,优先级越高(如果优先级相同,1. 发送广播的进程会优先接收 2. 先注册的Receiver先接收)。
    有序广播发送后,会被优先级最高的BroadCastReceiver接收,然后在处理完毕后依次被优先级较低的BroadCastReceiver接收,期间,BroadCastReceiver可以设置setResult将该广播的处理结果传递给下一个BroadCastReceiver,下一个Receiver通过getResult获取。有序广播被接收时,还可以通过abortBroadcast来终止,终止后,广播不会继续传递给其他Receiver。如:开发一个拦截短信的功能,我们将priority设置为1000,来最大限度的保证短信可以被我们的Receiver接收,在接收后,如果不想短信显示在系统短信列表中,我们可以abortBroadcast来终止传递,此时也就不会有短信提示音,也不会再短信收件箱中看到该短信。
    有序广播需要通过Context.sendOrderedBroadcast来发送

    粘性广播

    其实将粘性广播与有序广播/无序广播放在一起讲并不是非常合适,因为对它们的定义并不是在同一个维度上。
    粘性广播通过Context.sendStickyBroadcast()来发送,它与非粘性广播的区别是,在onReceiver中粘性广播不受10s限制(普通广播onReceiver在10s未处理完毕,会抛出ANR),粘性广播在10s后仍然存在,直至广播处理完毕。但需要注意的是,10s后,系统会将这个广播置为candidate状态,即可以被干掉的,当系统资源不足时,广播仍然可能会被丢弃。
    使用粘性广播需要android.Manifest.permission.BROADCAST_STICKY权限

    生命周期

    广播的生命周期从调用开始到onReceiver执行完毕结束,需要注意的是,一般广播的生命周期都极短,需要在10s内处理完onReceiver中的所有工作,所以,一般不进行耗时长的工作,如果有耗时长的工作,应当通过Intent传递给Service进行处理。(注意,不要在onReceiver中开启线程进行耗时任务处理,否则,在10s后,该线程会变成空线程,从而导致任务的丢失。同样的,也不要使用bindService来绑定服务。)
    值得注意的是,如果是在代码中动态注册的广播,如:在Activity注册,那么在Activity的onDestory中需要使用unregisterReceiver注销广播。

    实现原理

    在Android中,广播的出现是为了组件间的通信。其实在Android中,进程间通信有Binder,而同进程的通信方式就更多了,之所以使用广播,发送者与接受者都不需要知道对方的存在,这样带来的好处便是,系统的各个组件可以松耦合地组织在一起,这样系统就具有高度的可扩展性,容易与其它系统进行集成。
    Android中的广播使用了观察者模式:基于消息发布/订阅模式的事件驱动模型

    广播模型中包含三个角色:

    1. 消息发布者(广播发布者)
    2. 消息中心(AMS,即Activity Manager Service)
    3. 消息订阅者(广播接收者)
    1. 广播接受者通过Binder机制在AMS中注册监听
    2. 广播发布者通过Binder机制向AMS发送广播
    3. AMS根据发送者的需求,在已注册表中获取到合适的广播接受者(根据Intent-filter,Permission)
    4. AMS将广播发送给合适的广播接受者的消息循环队列中
    5. 广播接受者通过消息循环获取到广播并回调onReceive()

    整个广播发送与接收过程中,发送者与接收者是异步的,发送者不需要知道是否有接受者,也不需要知道接受者何时收到广播。

    使用方法

    广播的使用包括以下几个步骤:

    1. 自定义广播接收者:重写onReceiver()
    2. 注册广播接收者到消息中心(AMS):动态(代码中registerReceiver)或者静态注册(AndroidManifest.xml中申明)
    3. 定义及发送广播到消息中心(AMS):Context.sendBroadcast()、Context.sendOrderedBroadcast、Context.sendStickyBroadcast()
    4. AMS选择并发送广播给合适的广播接受者(根据Intent-filter,Permission)
    5. 广播接受者通过消息循环获取广播,并调用onReceive()进行处理

    以上1~3步骤需要用户进行处理,4、5步骤由Android系统完成。

    自定义广播接受者

    广播接受者的自定义很简单,继承BroadcastReceiver基类并重写onReceive()即可:

    // 继承BroadcastReceiver基类  
    public class MyBroadcastReceiver extends BroadcastReceiver {
      // 复写onReceive()方法
      // 接收到广播后,则自动调用该方法
      @Override
      public void onReceive(Context context, Intent intent) {
        // 接收广播后的操作
      }
    }
    
    注册广播接收者

    广播接受者的注册包含静态注册动态注册两种方式。
    静态注册 即在AndroidManifest.xml中通过<receive>标签进行申明。该方式注册的广播为常驻广播,注册后,即使应用处于非运行状态,也可以接收到广播。
    属性及实例:

    <receiver 
      android:enabled=["true" | "false"]
      android:exported=["true" | "false"] // 此broadcastReceiver能否接收其他App的发出的广播,默认值是由receiver中有无intent-filter决定:如果有intent-filter,默认值为true,否则为false
      android:icon="drawable resource"
      android:label="string resource"
      android:name=".MyBroadcastReceiver "  // 继承BroadcastReceiver子类的类名
      android:permission="string" // 具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
        android:process="string">  // BroadcastReceiver运行所处的进程 默认为与app同进程,可以指定独立的进程。注:Android四大基本组件都可以通过此属性指定自己的独立进程
     <intent-filter>
        <action android:name="android.net.conn.BOOT_COMPLETED" /> //用于指定此广播接收器将接收的广播类型,可以配置多条,这里给出的示例是android系统开机广播
      </intent-filter>
    </receiver>
    

    动态注册 即在代码中通过registerReceiver(BroadcastReceiver receiver, IntentFilter filter)来动态注册广播,该方法包含两个参数,receiver即我们自己定义的MyBroadcastReceiver,IntentFilter即需要过滤的条件。动态注册的广播在应用停止运行后无法接收广播,比如在ActivityA中注册,则应当在ActivityA销毁前,使用unregisterReceiver(BroadcastReceiver receiver)来注销注册

    MyBroadcastReceiver receiver=new MyBroadcastReceiver();  // 这里使用我们上面自定义的广播接受者
    IntentFilter filter=new IntentFilter();  
    filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");  // 这里以网络连接状态变化为例
    registerReceiver(receiver, filter);  
    
    定义及发送广播

    广播的发送有三种方法sendBroadcast(),sendOrderedBroadcast()和sendStickyBroadcast()
    示例如下:

    Intent intent = new Intent();  
    intent.setAction("com.itlao5.broadcast");  
    intent.putExtra("data", "data");  
    sendBroadcast(intent); 
    

    权限问题(安全性)

    看到这里,应该都很清楚如何发送及接受广播。但是在此过程中是否会存在一些问题,比如我发送的广播只想让指定的广播接收者接收,或者我只想接收指定发送者发送的广播,这些该如何实现呢?
    在上面广播注册一节中,我们可以看到有一个permission属性,没错,我们可以通过指定权限来实现上面的需求。

    1. 如何发送给指定的广播接受者?

    在AndroidManifest.xml中,我们可以配置

    <permission android:name = "com.android.permission.XXX"/> 
    

    然后在发送广播时

    sendBroadcast("com.android.XXX_ACTION", "com.android.permission.XXX");  
    

    此时,就只有在广播接收的应用中在AndroidManifest.xml中添加对应的XXX权限,才能正常接收到该广播。

    <uses-permission android:name="com.android.permission.XXX">></uses-permission>  
    
    1. 如何接收指定发送者的广播?

    在这种情况下,需要在接受者app的<receiver> tag中声明一下发送者app应该具有的权限。
    首先同上,在AndroidManifest.xml中定义新的权限SEND_XXX,例如:

    <permission android:name="com.android.SEND_XXX"/>  
    

    然后,在接受者app的Androidmanifest.xml中的<receiver> tag里添加权限SEND_XXX的声明

    <receiver android:name=".MyBroadcastReceiver"   
             android:permission="com.android.permission.SEND_XXX">   
       <intent-filter>  
            <action android:name="com.android.XXX_ACTION" />   
       </intent-filter>  
    </receiver> 
    

    如此,该接受者便只能接收来自具有该SEND_XXX权限的应用发出的广播。要发送该广播,只需在发送者app的AndroidManifest.xml中也声明使用该权限即可,如下:

    <uses-permission android:name="com.android.permission.SEND_XXX"></uses-permission>
    

    LocalBroadcast

    既然是说的广播,那么这里仅顺便提及一下LocalBroadcast。LocalBroadcast是本地广播,它的出现是为了应用内的广播传递。准确的说,LocalBroadcast与Broadcast不同,BroadCast基于Binder机制,而LocalBroadcast则是基于Handler机制。其机制的不同,导致他们的应用场景也不同,LocalBroadcast消耗资源少,安全性也相对来说更有保障,所以在应用内,更推荐使用LocalBroadcast。
    这里简单提一下,有兴趣的可以看这篇博文:Android开发之局部广播的使用——LocalBroadcast

    注意事项

    1. 动态注册的广播,在不需要使用时或者载体即将销毁时进行注销,即每一个registerReceiver需要有一个对应的unregisterReceiver
    2. 不要在广播接收器onReceive()中进行耗时操作,否则会引起ANR(10s)
    3. 不要在广播接收器onReceive()中开启异步任务,否则因为其生命周期的结束会出现空线程,导致任务丢失或者出现ANR等情况
    4. 耗时任务请开启service进行处理,且应当使用startService,而不应该使用bindService
    5. 应用内的广播尽量使用localBroadcast,因为其使用Handler,较Broadcast的Binder机制开销更小,且安全性更高
    6. 动态注册的广播优先度比静态注册高(当配置的优先级一致时),且可以控制其注册与注销,开销更小,所以能满足功能的情况下优先使用动态注册
    7. 如果接受不到自己发送的广播,请注意是否是因为权限问题

    相关文章

      网友评论

      本文标题:Android--四大组件之BroadCastReceiver

      本文链接:https://www.haomeiwen.com/subject/sridyftx.html