Android 广播与生活中的广播概念不同,它是指系统中产生事件后的通知。Android 广播不关心接收者是否收到处理或者如何处理广播,可以说是一种单向的通知。
Android 通过 BraodcastReceiver 来监听系统发出的广播,不同的 BraodcastReceiver 通过设置不同的 fliter 来区分监听广播的类型。有些广播的监听需要相应的权限。
BraodcastReceiver 必须经过注册才能具有监听功能,注册的方式有两种:
通过在 AndroidManifest.xml 中注册对应的 receiver,并设置要监听的 action
其中 MyReceiver 为继承 BroadcastReceiver 的类,重写了 onReceiver 方法,并在 onReceiver方法中对广播进行处理。标签设置过滤器,接收指定 action 广播。静态注册方式的特点:不管改应用程序是否处于活动状态,都会进行监听。action 可以为自定义 action,也可以是系统自带的 action。
动态注册是在程序中对 BroadcastReceiver 完成监听注册
MyReceiver receiver = new MyReceiver(); //创建过滤器,并指定 action,使之用于接收同 action 的广播 IntentFilter filter = new IntentFilter("MyReceiver_Action"); //注册广播接收器 registerReceiver(receiver, filter); 在 activity 中注册广播,必须在 activity 结束时注销广播,一般在 onStart 中注册 BroadcastReceiver,在 onStop 中取消 BroadcastReceiver,广播接收器跟随 Activity 的生命周期 //注销广播接收器 unregisterReceiver(receiver);
BroadcastReceiver 生命周期只有 8 秒左右,如果在 onReceive()内做超过 8 秒内的事情,就会报ANR(Application Not Response)程序无响应的错误信息。它的生命周期为从回调 onReceive()方法开始到该方法返回结果后结束。因此,如果要在收到广播后做耗时操作,最好放到 service 中去做,子线程也不好,当子线程未结束的时候,BroadcastReceiver 已经销毁。
广播被分为三种不同的类型:“普通广播(Normal broadcasts)”、“有序广播(Ordered broadcasts)”和“粘性广播(Sticky broadcast)”。普通广播是完全异步的,可以在同一时刻(逻辑上)被所有广播接收者接收到,消息传递的效率比较高,但缺点是:接收者不能将处理结果传递给下一个接收者,并且无法终止广播 Intent 的传播;然而有序广播是按照接收者声明的优先级别(声明在intent-filter 元素的 android:priority 属性中,数越大优先级别越高,取值范围:-1000 到 1000(其实最大可以为 int 最大值即:2147483647)。也可以调用 IntentFilter 对象的 setPriority()进行设置),被接收者依次接收广播。如:A 的级别高于 B,B 的级别高于 C,那么,广播先传给 A,再传给 B,最后传给 C。A 得到广播后,可以往广播里存入数据,当广播传给 B 时,B 可以从广播中得到 A 存入的数据。Context.sendBroadcast()发送的是普通广播,所有订阅者都有机会获得并进行处理。Context.sendOrderedBroadcast()发送的是有序广播,系统会根据接收者声明的优先级别按顺序逐个执行接收者,前面的接收者有权终止广播。BroadcastReceiver.abortBroadcast()如果广播被前面的接收者终止,后面的接收者就再也无法获取到广播。对于有序广播,前面的接收者可以将处理结果存放进广播 Intent,然后传给下一个接收者。Context.sendStickyBroadcast()是发送粘性广播,使用这个 api需要权限 android.Manifest.permission.BROADCAST_STICKY,粘性广播的特点是 Intent 会一直保留到广播事件结束,而这种广播也没有所谓的 8 秒限制,即上文所讲,如果 onReceive 方法执行时间太长,超过 8 秒的时候系统会将这个广播置为可以干掉的 candidate,一旦系统资源不够的时候,就会干掉这个广播而让它不执行。
接收无序广播的接收器接收到广播的顺序是有序的(由优先级决定顺序)
接收无序广播的接收器也一样可以设置优先级的
动态注册广播优先级高于静态注册广播
同等优先级的动态接收器,先注册的先接收
同等优先级的静态接收器,接收广播的顺序与 String[] java.io.File.list()顺序一致
Ps:这里有一点需要注意的是,同等优先级的静态接收器的接收顺序具有不确定性,原因就是File.list()的方法返回的顺序具有不确定性,如果需要查看某接收器的接收顺序,最好是试验大量的 apk 名
假设有如下优先级的 5 个接收器
1.动态 A(优先级=1)
2.动态 B(优先级=2)
3.动态 C(优先级=2)
4.静态 D(优先级=1)
5.静态 E(优先级=2)
并且 B 先于 C 注册
那么实际接收顺序应为
B C E A D
也就是说,如果静态接收器的优先级高于动态接收器的优先级,那么还是静态接收器先接收到广播(比如接收 SMS 广播)
动态接收器高优先级>动态接收器低优先级>静态接收器高优先级>静态接收器低优先级
有些广播,我们无法用静态接收器接收,比如 ACTION_SCREEN_ON,当屏幕被点亮的时候系统发送此广播。
1. void com.android.server.PowerManagerService.initInThread() 2. Java 代码 3. void initInThread() { 4. ?? 5. mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON); 6. mScreenOnIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 7. mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF); 8. mScreenOffIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 9. ?? 10. }
Intent 中都设置了 Intent.FLAG_RECEIVER_REGISTERED_ONLY,所以,如果要接收,必须动态注册广播接收器 ACTION_SCREEN_OFF 也是如此。
public static final int
FLAG_RECEIVER_REGISTERED_ONLY
Added in API level 1
If set, when sending a broadcast only registered receivers will be called -- no
BroadcastReceiver components will be launched.
Constant Value: 1073741824 (0x40000000)
ACTION_BATTERY_CHANGED(电池电量发生变化的时候,系统发送此广播)该广播就是这样
void com.android.server.BatteryService.sendIntent()
Java 代码 1. private final void sendIntent() { 2. // 3. Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); 4. intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY 5. Pack up the values and broadcast them to everyone 6. 7. }
静态 receiver 的注册是由 PackageManagerService 开机的时候负责初始 PMS 在开机的时候会对系统一些目录逐个扫描,解析 apk 文件。静态广播接收器就是在 PMS 做这件事情的时候顺便处理的。
PMS 会解析 apk 的 manifest 文件,查找这里注册的 receiver,然后加载到内存中。
PMS 初始化扫描目录的顺序:
system/framework
system/app
vendor/app
data/appd
rm/app-private
我们看到了 PMS 如何在初始化的时候如何解析 manifest 并把其中的 element 存放到内存中的其中receiver 保存到了 owner 的成员变量 receivers 中,owner 的类型是
android.content.pm.PackageParser.Package 也就是说 scanPackageLI 返回结果就是已经包含了manifest 信息的 Package 对象。
动态注册最终会调用到 AMS 中的 registerReceiver 函数,最终所有动态注册的 receiver 都保存到 AMS 的成员变量 mReceiverResolver 中。静态广播和动态广播如何注册的,我们已经全部分析完了。静态广播是 PackageManagerService负责,保存到其成员变量 mReceivers 中,动态广播是 ActivityManagerService 负责,保存到其成员变量 mReceiverResolver 中。
Context 中的 sendBroadCast 函数的实现是在 ContextImpl 中,和发送广播相关的有如下六个函数:
void android.app.ContextImpl.sendBroadcast(Intent intent) void android.app.ContextImpl.sendBroadcast(Intent intent, String receiverPermission) void android.app.ContextImpl.sendOrderedBroadcast(Intent intent, String receiverPermission) void android.app.ContextImpl.sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) void android.app.ContextImpl.sendStickyBroadcast(Intent intent) void android.app.ContextImpl.sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
可以分为 3 组:1 普通广播;2 Ordered 广播;3 Sticky 广播。不论哪种,最后都会由 AMS 处理,如果是非 ordered 广播,那么 mParallelBroadcasts 将存储所有动态接收器,然后合并的时候,mParallelBroadcasts 设置为 null,所以不会合并到 receivers 中,如果是 ordered 广播,那么mParallelBroadcasts 将合并到 receivers 中,然后,不管是哪种广播,最后都是调用scheduleBroadcastsLocked 继续处理,最终到 processNextBroadcast 函数上。广播是否有序,即通过 Boolean 变量 ordered 进行设置。
发送过程情况分为两种(scheduleBroadcastsLocked),ordered 广播和非 ordered 广播、非ordered 广播。先处理动接收器,然后处理静态接收器 ordered 广播同时处理动态接收器和静态接收器先将动态接收器与静态接收器合并,保持着与优先级相同的顺序,优先级高的在前面,否则顺序不变。静态接收器与动态接收器优先级相同的话,动态接收器在前。