基于Android10.0,分析广播机制的原理
一、概述
广播(BroadcastCast)用于进程/线程间的通信,广播有发送广播和接收广播两部分组成,其中广播接收者BroadcastReceiver是四大组件之一。
BroadcastReceiver分为两类:
静态广播:通过AndroidManifeset.xml的标签来注册BroadcastReceiver。
动态广播:通过AMS.registeredReceiver方式注册BroadcastReceiver,动态注册相对静态注册更加的灵活,在不需要时通过unregisteredReceiver来取消注册。
从广播的发送方式分为四种:
普通广播:完全异步的广播,在广播发出之后,所有的广播接收器几乎在同一时刻接收到这条广播消息,接收的先后顺序是随机的。通过Context.sendBroadcast发送。
有序广播:同步执行的广播。在广播发出去之后,同一时刻只有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑处理完成后,广播才可以继续传递。这种广播的接收顺序通过优先级(priority)设置,高的优先级先会收到广播。有序广播可以被接收者截断,使得后面的广播无法收到它。通过Context.sendOrderedBroadcast发送。
粘性广播:这种广播会一直滞留,当有匹配该广播的接收器被注册后,该接收器就会收到这个广播。粘性广播如果被销毁,下一次重建时会重新接收到消息数据。这种方式一般用来确保重要状态改变后的信息被持久的保存,并且能随时广播给新的广播接收器,比如:耗电量的改变。通过Context.sendStickyBroadcast发送。Android系统已经 @Deprecated该广播方式。
本地广播:发送处理的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收本应用程序发出的广播。通过LocalBroadcastManager.sendBroadcast发送。(基本原理是Handler,所以在AMS中没有本地广播的处理)
广播在系统中通过BroadcastRecord来记录。
1 | /** |
二、注册广播
注册广播一般可以在Activity/Service中调用registerReceiver方法,而这两者间接集成Context,其实现是ContextImpl。
2.1 CI.registerReceiver
[->ContextImpl.java]
1 | @Override |
其中broadcastPermission表示广播的权限,scheduler表示接收广播时onReceive执行的线程,当scheduler==null时代表在主线程中执行,大部分情况是不会指令scheduler。getOuterContext可以获取最外层调用者。
2.2 CI.registerReceiverInternal
[->ContextImpl.java]
1 | private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, |
2.3 LA.getReceiverDispatcher
[->LoadedApk.java]
1 | public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r, |
mReceivers定义为final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers;
mReceivers是以Context为key,以ArrayMap为value的ArrayMap。
2.3.1 创建ReceiverDispatcher
[->LoadedApk.java]
1 | ReceiverDispatcher(BroadcastReceiver receiver, Context context, |
mActivityThread为前面传过来的Handler。
2.3.2 创建InnerReceiver
[->LoadedApk.java]
1 | final static class InnerReceiver extends IIntentReceiver.Stub { |
InnerReceiver继承于IIntentReceiver.Stub,是一个服务器端,广播分发者通过getReceiverDispatcher可以获取该Binder服务端对象InnerReceiver,用于IPC通信。
2.4 AMS.registerReceiver
[->ActivityManagerService.java]
1 | public Intent registerReceiver(IApplicationThread caller, String callerPackage, |
mReceiverResolver记录着所有已经注册的广播,是以receiver IBinder为key, ReceiverList为value的ArrayMap。
在BroadcastQueue中有两个广播队列mParallelBroadcasts、mOrderedBroadcasts,类型为ArrayList
mParallelBroadcasts:并行广播队列,可以立刻执行,而无需等待另一个广播运行完成,该队列只允许动态已注册的广播,从而避免发生同时拉起大量进程来执行广播,前台和后台的广播分别位于独立的队列。
mOrderedBroadcasts:有序广播,同一时间只允许执行一个广播,该队列头部的广播就是活动广播,其他广播必须等待该广播结束才能运行,也是独立区别前台和后台的广播。
2.4.1 AMS.getRecordForAppLocked
[->ActivityManagerService.java]
1 | ProcessRecord getRecordForAppLocked(IApplicationThread thread) { |
mLruProcesses 定义为ArrayList
2.4.3 IntentFilter.match
[->IntentFilter.java]
1 | public final int match(ContentResolver resolver, Intent intent, |
该方法用于匹配Intent的数据是否成功,匹配的数据包括action,type,data,category四项,任何一项匹配不成功都会失败。
2.4.3 AMS.broadcastQueueForIntent
[->ActivityManagerService.java]
1 | BroadcastQueue broadcastQueueForIntent(Intent intent) { |
broadcastQueueForIntent通过判断intent中是否包含FLAG_RECEIVER_FOREGROUND来决定是前台广播还是后台广播
2.5 小结
注册广播:
1.注册广播的参数为广播BroadcastReceiver和过滤添加IntentFilter;
2.创建对象LoadedApk.ReceiverDispatcher.InnerReceiver,该对象集成于IIntentReceiver.Stub;
3.通过AMS把当前进程的ApplicationThread和InnerReceiver对象的代理类,注册到systemserver进程;
4.当广播receiver没有注册时,则创建广播接收者队列ReceiverList,该对象集成于ArrayList,并添加到AMS.mRegisteredReceivers(已注册广播队列);
5.创建BroadcastFilter队列,并添加到AMS.mReceiverResolver
6.将BroadcastFilter添加到广播接收者的ReceiverList
当注册的广播为Sticky广播:
1.创建BroadcastRecord,并添加到BroadcastQueue中的mParallelBroadcasts,注册后,调用AMS处理该广播
2.根据注册的intent中是否包含FLAG_RECEIVER_FOREGROUND,包含则是mFgBroadcastQueue队列,否则为mBgBroadcastQueue队列。
三、发送广播
和注册广播一样,最后调用的是ContextImpl.sendBroadcast
3.1 CI.sendBroadcast
[->ContextImpl.java]
1 | @Override |
3.2 AMS.broadcastIntent
[->ActivityManagerService.java]
1 | public final int broadcastIntent(IApplicationThread caller, |
broadcastIntent有两个boolean值参数serialized、sticky共同决定是普通广播,有效广播,还是sticky广播。
类型 | serialized | sticky |
---|---|---|
sendBroadcast | false | false |
sendOrderedBroadcast | true | false |
sendStickyBroadcast | false | true |
3.3 AMS.broadcastIntentLocked
[->ActivityManagerService.java]
1 | @GuardedBy("this") |
该方法比较长,这里分为8部分进行分析。
3.3.1 设置广播flags
1 | intent = new Intent(intent); |
这个过程主要的工作如下:
1.是否设置FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS标志,如果设置广播对即时应用可见;
2.添加flagFLAG_EXCLUDE_STOPPED_PACKAGES,保证已经停止的app不会收到广播;
3.当系统还没有启动完成,不允许启动新进程;
4.非USER_ALL广播且接收广播的用户没有处于Running的情况下,除非是系统升级广播和关键广播,否则直接返回。
BroadcastReceiver还有其他flag,位于Intent.java常量中:
1 | FLAG_RECEIVER_REGISTERED_ONLY //只允许已经注册的receiver接收广播 |
3.3.2 广播权限验证
1 | final String action = intent.getAction(); |
主要工作如下:
1.如果TemporaryAppWhitelistDuration>0,检查是否有CHANGE_DEVICE_IDLE_TEMP_WHITELIST权限,没有抛出异常
2.检查是否后台限制发送广播,如果限制,则后台应用将不能发送广播
3.callingUid为 ROOT_UID, SYSTEM_UID,PHONE_UID,BLUETOOTH_UID,NFC_UID,SE_UID,NETWORK_STACK_UID和persistent进程时,可以发送受保护广播
4.为非系统应用发送广播时:当发送的是受保护的广播,则抛出异常;当action为ACTION_APPWIDGET_CONFIGURE或ACTION_APPWIDGET_UPDATE时,限制该广播只发送给自己,否则抛出异常。
5.如果允许后台应用接收广播,则添加FLAG_RECEIVER_INCLUDE_BACKGROUND
3.3.3 处理系统相关广播
1 | switch (action) { |
该过程主要系统广播,主要是package、时间、网络相关的广播,并对这些广播进行相应的处理。
3.3.4 增加sticky广播
1 | // Add to the sticky list if requested. |
这个过程主要是检查sticky广播,将sticky广播放入到mStickyBroadcasts,并增加到list
3.3.5 查询receivers和registeredReceivers
1 | //发送广播的user |
主要工作如下:
1.根据userId判断发送的是全部的接收者还是指定的userId
2.查询广播,并将其放入到两个列表:
registeredReceivers:来匹配当前intent的所有动态注册的广播接收者(mReceiverResolver见2.4节)
receivers:记录当前intent的所有静态注册的广播接收者
1 | private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType, |
3.3.6 处理并行广播
1 | //用于标识是否需要用新的intent替换旧的intent |
广播队列中有一个mParallelBroadcasts变量,类型为ArrayList,记录所有的并行广播
1 | public void enqueueParallelBroadcastLocked(BroadcastRecord r) { |
3.3.7 合并registeredReceivers到receivers
1 | // Merge into one list. |
这里主要是将动态注册的registeredReceivers(如果发送的广播不是有序广播则registeredReceivers = null)全部合并到receivers,再统一按照串行方式处理。
3.3.8 处理串行广播
1 | if ((receivers != null && receivers.size() > 0) |
广播队列中有一个mOrderedBroadcasts变量,类型为ArrayList,记录所有的有序广播
1 | //串行广播加入到mOrderedBroadcasts队列 |
3.4 小结
发送广播的过程:
1.默认不发送给已停止的(FLAG_EXCLUDE_STOPPED_PACKAGES)应用和即时应用(需要添加该FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS标记才可以)
2.对广播进行权限验证,是否是受保护的广播,是否允许后台接收广播,是否允许后台发送广播
3.处理系统广播,主要是package、时间、网络相关的广播
4.当为粘性广播时,将sticky广播增加到list,并放入mStickyBroadcasts队列
5.当广播的intent没有设置FLAG_RECEIVER_REGISTERED_ONLY,则允许静态广播接收者来处理该广播;创建BroadcastRecord对象,并将该对象加入到相应的广播队列,然后调用BroadcastQueue的scheduleBroadcastsLocked方法来完成不同广播的处理。
不同广播的处理方式:
1.sticky广播:广播注册过程中处理AMS.registerReceiver,开始处理粘性广播,见2.4节
- 创建BroadcastRecord对象
- 添加到mParallelBroadcasts队列
- 然后执行 queue.scheduleBroadcastsLocked()
2.并行广播:广播发送处理过程见3.3.6节
- 只有动态注册的registeredReceivers才会进行并行处理
- 创建BroadcastRecord对象
- 添加到mParallelBroadcasts队列
- 然后执行 queue.scheduleBroadcastsLocked()
3.串行广播:广播发送处理过程见3.3.8节
- 所有静态注册的receivers以及动态注册的registeredReceivers(发送的广播是有序广播)合并到一张表处理
- 创建BroadcastRecord对象
- 添加到mOrderedBroadcasts队列
- 然后执行 queue.scheduleBroadcastsLocked()
从上面可以看出,不管哪种广播方式,都是通过broadcastQueueForIntent来根据intent的flag判段是前台队列还是后台队列广播,然后再调用对应广播队列的scheduleBroadcastsLocked方法来处理广播。
四、接收广播
在发送广播的过程会执行scheduleBroadcastsLocked方法来处理广播
4.1 BQ.scheduleBroadcastsLocked
[->BroadcastQueue.java]
1 | public void scheduleBroadcastsLocked() { |
4.1.1 BroadcastHandler
[->BroadcastQueue.java]
1 | private final class BroadcastHandler extends Handler { |
发送BROADCAST_INTENT_MSG消息后,BroadcastHandler进行处理,其初始化在构造函数中创建,而BroadcastQueue是在AMS初始化,从源码中可以看出handler采用的是”ActivityManagerService”线程的loop。
1 | BroadcastQueue(ActivityManagerService service, Handler handler, |
4.2 BQ.processNextBroadcast
[->BroadcastQueue.java]
1 | final void processNextBroadcast(boolean fromMsg) { |
此次mService为AMS,整个流程比较长,全程持有AMS锁,所以广播效率低下的情况下,直接会严重影响手机的性能和流畅度,这里是否应考虑细化同步锁的粒度。
1 | final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) { |
整个处理的过程比较长,将分为四个部分进行分析。
4.2.1 处理并行广播
1 | BroadcastRecord r; |
通过while循环,每次取出一个,一次性分发完所有的并发广播后,将分发完成的添加到历史广播队列。
4.2.2 处理串行广播
1 | // Now take care of the next serialized one... |
mTimeoutPeriod,对于前台广播为10s,后台广播为60s。广播超时为2 mTimeoutPeriod numReceivers,接收者个数numReceivers越多,则广播超时总时间越大。
4.2.3 获取下条有序广播
1 | //获取下条广播的index |
发送广播超时,如果是动态注册的广播则执行deliverToRegisteredReceiverLocked方法,如果是静态注册的广播,则进行一系列的权限检查,不满足则跳过该广播。
4.2.4 处理下条有序广播
1 | // Is this receiver's application already running? |
如果是动态广播接收者,则调用deliverToRegisteredReceiverLocked处理
如果是静态广播接收者,且对应进程已经创建,则调用processCurBroadcastLocked处理
如果是静态广播接收者,且对应进程未创建,则调用startProcessLocked创建进程
4.3 BQ.deliverToRegisteredReceiverLocked
[->BroadcastQueue.java]
1 | private void deliverToRegisteredReceiverLocked(BroadcastRecord r, |
这里是检查动态注册的广播的相关权限,如果是有序广播则跳过。最后执行performReceiveLocked处理并行广播。
4.4 BQ.performReceiveLocked
[->BroadcastQueue.java]
1 | void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, |
通过app.thread获取到IApplicationThread代理类,通过这个代理类,访问到ApplicationThread中的方法。
4.5 AT.scheduleRegisteredReceiver
[->ActivityThread.java::ApplicationThread]
1 | // This function exists to make sure all receiver dispatching is |
receiver是注册广播时创建的,见2.3.3节 receiver=LoadedApk.ReceiverDispatcher.InnerReceiver
4.6 IR.performReceive
[->LoadedApk.java::InnerReceiver]
1 | public void performReceive(Intent intent, int resultCode, String data, |
4.7 RD.performReceive
[->LoadedApk.java::ReceiverDispatcher]
1 | public void performReceive(Intent intent, int resultCode, String data, |
Args继承于BroadcastReceiver.PendingResult,实现了Runnable,其中mActivityThread是当前的主线程,在2.3.1节完成赋值,post消息机制,将消息放入MessageQueue,该消息是Runnable,而后执行该Runnable。
4.8 ReceiverDispatcher.Args.getRunnable
[->LoadedApk.java]
1 | public final Runnable getRunnable() { |
4.9 PendingResult.finish
[->BroadcastReceiver.java::PendingResult]
1 | /* Finish the broadcast. The current result will be sent and the |
主要工作:
1.静态注册的广播接收者:
当QueuedWork工作未完成时,则等待完成再执行sendFinished
当QueuedWork工作已经完成,直接调用sendFinished方法
2.动态注册的广播接收者:当发送的是串行广播,则直接调用sendFinished
参数说明:
TYPE_COMPONENT:静态注册
TYPE_REGISTERED:动态注册
TYPE_UNREGISTERED:取消注册
4.9.1 PendingResult.sendFinished
[->BroadcastReceiver.java::PendingResult]
1 | /** @hide */ |
4.9.2 AMS.finishReceiver
[->ActivityManagerService.java]
通过binder机制最后调用到AMS的finishReceiver方法。
1 |
|
4.9.3 BQ.finishReceiverLocked
[->BroadcastQueue.java]
1 | public boolean finishReceiverLocked(BroadcastRecord r, int resultCode, |
这个过程主要是设置BroadcastRecord各个参数为null,并判断是否还有广播需要处理。
4.10 小结
接收广播的过程,主要是对并行广播队列和串行广播队列的处理,先处理并行广播队列后处理串行广播队列:
1.处理广播队列,对广播队列进行相应的权限检查处理。无论是并行还是串行,最后调用的都是performReceiveLocked方法。其中串行广播还有时间限制,会调用broadcastTimeoutLocked方法,超时会强制结束广播;
2.通过Binder机制,将消息传递给接收广播的进程进行处理,如果接收广播的进程没起来,还需要启动其进程;
3.最后回调receiver.onReceive的方法,对于串行广播还需要通知AMS已经处理完该条广播,并进行下个广播的处理。
五、总结
发送广播过程的流程如下:
广播机制
1.当发送串行广播(order= true)时
静态注册的广播接收者(receivers),采用串行处理
动态注册的广播接收者(registeredReceivers),采用串行处理
2.当发送并行广播(order= false)时
静态注册的广播接收者(receivers),采用串行处理
动态注册的广播接收者(registeredReceivers),采用并行处理
静态注册的receiver都是采用串行处理;动态注册的registeredReceivers处理方式无论是串行还是并行,取决于广播的发送方式(processNextBroadcast);静态注册的广播由于其所在的进程没有创建,而进程的创建需要耗费系统的资源比较多,所以让静态注册的广播串行化,防止瞬间启动大量的进程。
广播ANR只有在串行广播时才需要考虑,因为接收者是串行处理的,前一个receiver处理慢,会影响后一个receiver;并行广播通过一个循环一次性将所有的receiver分发完,不存在彼此影响的问题,没有广播超时。
串行超时情况:某个广播处理时间>2receiver总个数mTimeoutPeriod,其中mTimeoutPeriod,前后队列为10s,后台队列为60s;某个receiver的执行时间超过mTimeoutPeriod。
附录
源码路径
1 | frameworks/base/services/core/java/com/android/server/am/BroadcastRecord.java |