Android BroadcastReceiver - tenji/ks GitHub Wiki

Android BroadcastReceiver

一、BroadcastReceiver 是什么?

Android App 可以发送广播也可以接收系统或者其它 App 发送的广播,是发送/订阅的设计模式。这些广播被发送当重要的事件发生的时候。例如,当各种各样系统事件发生的时候安卓系统发送广播,比如手机启动了或者手机开始充电了。应用也可以发送自定义广播,例如通知其它应用一些他们可能感兴趣的东西,比如一些新的内容被下载了。

系统广播会在系统事件发生的时候被发送出来,比如当手机进入或者退出开发者选项的时候,所有订阅了系统广播的人都可以收到这个广播。

广播它自身是被包裹在了一个 Intent 里面,它是有一个唯一的标识的(例如 android.intent.action.AIRPLANE_MODE)。这个 Intent 对象同时包含了一些其它的信息,在它的字段里面,飞行模式这个 intent 里面就包含了一个 boolean 的字段来表示飞行模式是开启还是关闭的。

二、BroadcastReceiver 能用来做什么?

  1. 接收系统发送出的重要的广播(网络变化,开机,充电);
  2. App 之间相互通信,相互拉活的手段;
  3. App 内部组建间通信的手段。

三、BroadcastReceiver 分为哪几类?

3.1 普通广播(Normal Broadcast)

又称无序广播,这种广播完全是异步的,所有与广播 Intent 匹配的 BroadcastReceiver,都可以收到这条广播,并且不分先后顺序,视为同时收到,通过 Context.sendBroadcast() 方法发送。这种广播的效率比较高,但缺点是接收器不能将处理结果传递给下一个接收器,并且无法在中途终止广播。

Intent intent = new Intent();
//对应BroadcastReceiver中intentFilter的action
intent.setAction("test");
//发送广播
sendBroadcast(intent);

3.2 有序广播(Ordered Broadcast)

这是一种同步执行的广播,通过 Context.sendOrderedBroadcast() 方法发送,这种广播发出后,通过 receiver 的 intent-filter 中的 android:priority 属性来设置优先级,优先级从-1000~1000,数越大,优先级越高,使用 setResult() 方法把结果传递给下一个接收者,通过 getResult() 方法获取上一个接收者传递过来的结果,并可以通过 abortBroadcast() 方法丢弃该广播,使该广播不再传递给下一个接收者。

3.3 粘性广播(Sticky Broadcast)

粘性广播通过 Context.sendStickBroadcast() 方法来发送,用此方法发送的广播会一直滞留,当有匹配此广播的接收器被注册后,该广播接收器就会收到此广播。使用此广播时,需要获得 BROADCAST_STICKY 权限。(在 android 5.0/api 21后不再推荐使用)

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

3.4 系统广播(System Broadcast)

Android 系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。如:开启启动,网络状态改变,拍照,屏幕关闭与开启,点亮不足等等。每个系统广播都具有特定的 intent-filter,其中主要包括具体的 action,系统广播发出后,将被相应的 BroadcastReceiver 接收。系统广播在系统内部当特定事件发生时,有系统自动发出。

3.5 本地广播(Local Broadcast)

以上广播都属于全局广播,发出去的广播,只要有匹配的接收者,就可以收到广播。这样一来会造成一些问题,一是消耗性能,二是容易引起安全性的问题,为了能够简单的解决这方面的问题,Android 引入了一套广播本地广播机制,使用该机制发出的广播只能够在本应用内部进行传递,并且广播接收器也只能接收来自本应用发出的广播。

四、BroadcastReceiver 怎么使用?

4.1 注册广播

  • 静态注册(常驻)

    静态注册就是在 AndroidManifest 中注册 BroadcastReceiver,并指定它所接收的广播种类,如下面配置的 MyReceiver 用来接收开机广播:

    <receiver android:name=".MyReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
  • 动态注册(非常驻)

    @Override
    protected void onResume() {
        super.onResume();
        mReceiver = new MyReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("test");
        registerReceiver(mReceiver, intentFilter);  // 注册广播接收器
    }
    
    @Override
    protected void onPause() {
        unregisterReceiver(mReceiver);  // 注销广播接收器
        super.onPause();
    }

本文用到的 BroadcastReceiver:

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

    }
}

4.2 发送广播

待更新...

4.3 接收广播

待更新...

五、BroadcastReceiver 在不同版本的 API 中有哪些区别?

5.1 Android 9

从 Android 9 (API level 28) 开始,NETWORK_STATE_CHANGED_ACTION 广播不会携带用户的地理位置信息或者个人身份数据。此外,当你的 App 运行在 Android 9 或者更高的手机上,系统的 Wifi 广播也不会携带 SSIDs, BSSIDs,连接信息或扫描结果。想获取以上信息,需要通过 getConnectionInfo() 来代替。

5.2 Android 8

从 Android 8 (API level 27) 开始,系统加强了对静态广播的进一步限制,许多广播静态注册了也是收不到的,不过你可以采用动态注册的方式来接收这些广播。

5.3 Android 7

从 Android 7 (API level 24) 开始,系统不会再发送 ACTION_NEW_PICTURE, ACTION_NEW_VIDEO 的广播。同时从7.0开始 App 想要接受 CONNECTIVITY_ACTION 广播,需要通过动态注册广播的形式了,再通过静态广播注册的方式是不可以的了。

六、BroadcastReceiver 发送广播和接收广播是怎样工作的?

待更新...

七、BroadcastReceiver 是怎么引发 ANR 的?

众所周知广播是会造成 ANR 的,造成 ANR 就是因为发送方将广播发送给 AMS (ActivityManagerService),然后 AMS 找有没有人注册,找到之后让它去执行,在执行开始之前 AMS 就开始为 ANR 进行耗时统计了,如果这个时候 App 进程已经存在,那么便把这个消息加入到消息队列中,等待调度,最后执行完成。如果 App 不存在,AMS 会拉活我们的进程,然后我们的 App 会执行这个消息,所以如果我们被广播拉活,我们的启动时长也是会被统计到 ANR 的时间范围内的。

参考链接

⚠️ **GitHub.com Fallback** ⚠️