从源码理解BroadcastReceiver的工作过程
这篇文章原本应该是继续看
VirtualApk中关于插件BroadcastReceiver的解决的。不过因为解决逻辑比较简单(在加载插件的时候把插件的所有BroadcastReceiver转为动态广播并注册),所以这里就不看了。
本文就从Android源码(8.0)来看一下系统对
BroadcastReceiver的解决逻辑(广播接收者注册、发送广播),BroadcastReceiver的源码解决逻辑很多也很复杂,我们只看重点,所以对于广播少量很细致的点是看不到了。本文的目标是理解系统对广播的整个解决的过程。
BroadcastReceiver的注册
动态注册广播接收者
我们从动态注册开始看 : context.registerReceiver(mBroadcastReceiver, intentFilter), 最终调用的方法是ContextImpl.registerReceiverInternal():
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context, int flags) { IIntentReceiver rd = null; ... rd = new LoadedApk.ReceiverDispatcher( receiver, context, scheduler, null, true).getIIntentReceiver() ... ActivityManager.getService().registerReceiver( mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId, flags)}即构造了一个LoadedApk.ReceiverDispatcher, 而后就转到ActivityManagerService去注册。那LoadedApk.ReceiverDispatcher是什么呢?
LoadedApk : 这个类是用来保存当前运行app的状态的类,它保存着app的Application、类加载器、receiver、service等信息。
LoadedApk.ReceiverDispatcher : 这个类含有一个InnerReceiver(Stub Binder),用来和服务端通信,当ActivityManagerService分发广播时,就会通过这个(Stub)Binder调用BroadcastReceiver.onReceiver()。这个我们到后续看广播接收的时候再讲。先知道这个类可以被ActivityManagerService用来和用户端通信就可。
ActivityManagerService.registerReceiver()
这个方法的注册逻辑也比较简单,这里我们不看粘性广播(已被废弃)的注册部分:
public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter ...) { ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder()); if (rl == null) { rl = new ReceiverList(this, callerApp, callingPid, callingUid, userId, receiver); if (rl.app != null) { ... rl.app.receivers.add(rl); } else { ... } mRegisteredReceivers.put(receiver.asBinder(), rl); } BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission, callingUid, userId, instantApp, visibleToInstantApps); rl.add(bf); mReceiverResolver.addFilter(bf); }即把一个BroadcastFilter放入ReceiverList和mReceiverResolver中。那这两个又是什么呢?
BroadcastFilter : 它是IntentFilter的子类,即一个BroadcastReceiver的IntentFilter,保存少量BroadcastReceiver特有的少量信息,比方权限等。
ReceiverList : 我们知道一个BroadcastReceiver可以有多个BroadcastFilter(IntentFilter)。它是用来保存一个BroadcastReceiver的BroadcastFilter列表的。 mRegisteredReceivers是一个保存ReceiverList的map。它的key是一个Binder,即LoadedApk.ReceiverDispatcher中的InnerReceiver(Stub Binder)。value就是ReceiverList。Binder作为key是为了方便和BroadcastReceiver的用户端通信。
- mReceiverResolver
看一下它的类型 : IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
IntentResolver这个类还是比较熟习的,它可以解析一个intent。 我们知道可以使用IntentFilter来匹配一个Intent。BroadcastFilter就是来匹配BroadcastReceiver的Intent。mReceiverResolver里面维护了一个BroadcastFilter列表。所以mReceiverResolver就是可以用来解析一个广播的Intent。找出其匹配的BroadcastReceiver。
即注册过程可以使用下图表示:
BroadcastReceiver的注册.png
即广播的注册过程就是把注册的BroadcastFilter(IntentFilter)放到系统的BroadcastFilter维护列表(mRegisteredReceivers和mReceiverResolver)中。目的是为了在接收广播时好找到对应的广播接收者
BroadcastReceiver的接收
在我们注册了BroadcastReceiver之后,系统在收到广播时,是如何正确的分发的呢?还是先找一个入口点,我们从发送一个无序广播ContextImpl.sendBroadcast()开始看:
public void sendBroadcast(Intent intent) { ... ActivityManager.getService().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false, getUserId());}即直接转发到了ActivityManagerService.broadcastIntentLocked(), 这个方法很长,我们去除对于系统特殊广播和粘性广播接收逻辑的解决来看:
int broadcastIntentLocked(...Intent intent, String resolvedType, IIntentReceiver resultTo, ...,boolean ordered,...){ ... 对于特定系统广播的分发解决 以及 粘性广播的解决 List receivers = null; // manifest注册的广播 List<BroadcastFilter> registeredReceivers = null; // 动态注册 //动态注册的广播接收者也可以接收这个广播 if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) { receivers = collectReceiverComponents(intent, resolvedType, callingUid, users); //收集在Manifest中注册的可以接收这个intent的广播接收者 } //没有显示指明广播接收者 if (intent.getComponent() == null) { if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) { ...对于一律客户符合条件的广播接收者的收集 }else{ //收集当前的符合条件的广播接收者。 mReceiverResolver保存着动态注册的广播信息 registeredReceivers = mReceiverResolver.queryIntent(intent,resolvedType, false /*defaultOnly*/, userId); } } //解决代码动态注册的广播 int NR = registeredReceivers != null ? registeredReceivers.size() : 0; if (!ordered && NR > 0) { ... //这个queue的作用是把广播分发给广播接收者 final BroadcastQueue queue = broadcastQueueForIntent(intent); //利用 registeredReceivers 构建一个 BroadcastRecord BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, callerInstantApp, resolvedType, requiredPermissions, appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId); ... queue.enqueueParallelBroadcastLocked(r); //并发分发广播到广播接收者 queue.scheduleBroadcastsLocked(); } ... 根据广播接收者的 priority 调整 receivers中广播接收者的顺序 //解决manifest静态注册的广播 if ((receivers != null && receivers.size() > 0) || resultTo != null) { BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, callerInstantApp, resolvedType, requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId); .... queue.enqueueOrderedBroadcastLocked(r); //串行分发广播到广播接收者 queue.scheduleBroadcastsLocked(); }} 大体逻辑我就不解释了,上面代码还是加了挺详细的注释的。可以看到最终对于广播的分发过程是:
- 根据一个广播的
Intent获取对应的BroadcastQueue - 根据一个广播接收者列表创立一个
BroadcastRecord - 把
BroadcastRecord增加到BroadcastQueue中 BroadcastQueue开始分发广播给广播接收者queue.scheduleBroadcastsLocked()
- BroadcastQueue
源码中一共存在两个BroadcastQueue, 一个是前端广播队列(mFgBroadcastQueue),一个是后端广播队列(mBgBroadcastQueue)。这两广播队列最直接的区别是mFgBroadcastQueue在分发广播时超时时间为10s,mBgBroadcastQueue在分发广播时超时时间是60s。
所以我们继续看BroadcastQueue是如何把广播分发给广播接收者的queue.scheduleBroadcastsLocked(), 这个方法最终调用的是BroadcastQueue.processNextBroadcastLocked(),这个方法代码也很长,分成两个部分来看:
无序广播的分发
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) { BroadcastRecord r; while (mParallelBroadcasts.size() > 0) { //解决广播接收者并行集合 r = mParallelBroadcasts.remove(0); // 取得一个`BroadcastRecord` .... final int N = r.receivers.size(); for (int i=0; i<N; i++) { //分发广播给 广播接收者 deliverToRegisteredReceiverLocked(r, (BroadcastFilter)r.receivers.get(i), false, i); } ... } ...}逻辑很简单,即从mParallelBroadcasts取出一个BroadcastRecord而后调用deliverToRegisteredReceiverLocked去分发,它里面的逻辑大部分都是权限判断和对无序广播跳过,因而不看它的具体内容了,它最终会调用到performReceiveLocked:
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,Intent intent, ...) throws RemoteException { if (app != null) { if (app.thread != null) { app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode, data, extras, ordered, sticky, sendingUser, app.repProcState); } } else { receiver.performReceive(intent, resultCode, data, extras, ordered, sticky, sendingUser); } }即假如广播接收者所在的主线程不为null,即直接通过ApplicationThread(Binder)切换到这个进程的主线程去接收这个广播,否则通过IIntentReceiver(Binder)切换到对应的进程去接收广播。最终的结果都是实例化一个BroadcastReceiver,在主线程调用其onReceiver方法。这两条路径最终都会走到一个地方,而后调用下面代码(其实有序广播最终也是来到这个地方):
//运行在主线程 ClassLoader cl = mReceiver.getClass().getClassLoader(); intent.setExtrasClassLoader(cl); intent.prepareToEnterProcess(); setExtrasClassLoader(cl); receiver.setPendingResult(this); receiver.onReceive(mContext, intent); ... finish(); //即实例化BroadcastReceiver,调用onReceive()。 最后调用finish()。这个方法很关键,由于它负责再告诉ActivityManagerService,这个广播解决完毕了:
if (mOrderedHint) { am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,mAbortBroadcast, mFlags); } else { am.finishReceiver(mToken, 0, null, null, false, mFlags); }所以我们再回到ActivityManagerService来看一下:
public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle resultExtras, boolean resultAbort, int flags) { BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0 ? mFgBroadcastQueue : mBgBroadcastQueue; r = queue.getMatchingOrderedReceiver(who); if (r != null) { doNext = r.queue.finishReceiverLocked(r, resultCode, resultData, resultExtras, resultAbort, true); } if (doNext) { r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true); } ....}通过用户端传过来的参数,可以看出这个方法其实只是对有序广播做了解决,对无序广播并没有做解决。也可以猜出,对有序广播解决的起因是要保证接下来广播可以继续解决。好到这里,无序广播的分发流程就看完了。
有序广播的分发
按照我们前面看的广播注册的源码,有序广播是指指定了广播的priority属性。BroadcastQueue.mOrderedBroadcasts会把BroadcastRecord按照这个顺序依次排列。因而解决有序广播其实就是把mOrderedBroadcasts的BroadcastRecord拿出来一个一个的解决。这里还是从BroadcastQueue.processNextBroadcastLocked()一点一点的来看 :
BroadcastRecord r; //确定要分发的有序广播,假如在遍历过程中发现了超时的广播,则直接强制分发 do { ... r = mOrderedBroadcasts.get(0); //超时会强制分发广播 forceReceive = true if (... || forceReceive) { ... performReceiveLocked(r.callerApp, r.resultTo,new Intent(r.intent), r.resultCode,...); ... r = null; continue; } } while (r == null); // nextReceiver 其实就是一个index, 我们知道 BroadcastRecord 是有一个广播接收者列表的 int recIdx = r.nextReceiver++; final Object nextReceiver = r.receivers.get(recIdx); //拿出一个广播接收者 if (nextReceiver instanceof BroadcastFilter) { //解决动态注册的广播接收者 BroadcastFilter filter = (BroadcastFilter)nextReceiver; //分发这个广播给这个广播接收者 deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx); return; //前面看无序广播的时候已经知道,要接收到前一个广播接收者接收完成的信号才会继续分发有序广播 } ResolveInfo info = (ResolveInfo)nextReceiver; //静态注册的广播接收者 ...一系列的权限判断,假如有问题直接跳过 //广播接收者所在的进程正在运行 if (app != null && app.thread != null && !app.killed) { .... processCurBroadcastLocked(r, app, skipOomAdj); ... return; } //尝试唤醒广播接收者所在的进程 if ((r.curApp=mService.startProcessLocked(targetProcess...) == null) { ..唤起失败 scheduleBroadcastsLocked(); //直接解决下一个 return; } //进程唤起成功,把广播设置为 pending mPendingBroadcast = r; mPendingBroadcastRecvIndex = recIdx;其实上面的注释我已经写的挺清楚的了。所以这里不做过多的详情。deliverToRegisteredReceiverLocked()这个方法就是前面分发无序广播的方法。所以不再看了,我们看一下processCurBroadcastLocked() :
private final void processCurBroadcastLocked(BroadcastRecord r, ProcessRecord app, boolean skipOomAdj) throws RemoteException { ... app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver, mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo), r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId, app.repProcState); ... }很简单,即还是回到了广播的进程去实例化广播,调用其onReceive方法。到这里可以知道: 有序广播和无序广播在用户端的解决是一样的。那一个有序广播用户端解决完毕之后怎样办呢? 前面在看无序广播的时候已经知道会
再次回到ActivityManagerService,调用finishReceiver()方法。这个方法我们前面已经贴过了,不过我们再把它的主要逻辑贴出来:
BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0 ? mFgBroadcastQueue : mBgBroadcastQueue; r = queue.getMatchingOrderedReceiver(who); if (r != null) { doNext = r.queue.finishReceiverLocked(r, resultCode,resultData, resultExtras, resultAbort, true); } if (doNext) { r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true); }processNextBroadcastLocked()这个方法是分发广播的入口,我们不再看了。看一下r.queue.finishReceiverLocked() :
public boolean finishReceiverLocked(BroadcastRecord r, int resultCode, String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) { ...清理状态 if (r.nextReceiver < r.receivers.size()) { Object obj = r.receivers.get(r.nextReceiver); nextReceiver = (obj instanceof ActivityInfo) ? (ActivityInfo)obj : null; } else { nextReceiver = null; } .... }即清理了少量状态,而后确定了这个BroadcastRecord的下一个BroadcastReceiver。后续会继续分发广播给这个BroadcastReceiver。
即有序广播的分发通过上面的机制会依次分发给广播接收者。
看完一遍源码,弄的云里雾里的,因而使用下面这张图来理清整个系统的广播解决机制:
Android广播接收者解决逻辑.png
LocalBroadcastManager
平常假如我们只是在app内使用广播来做简单的通知等,可以使用它来注册广播接收者和发送广播。它会自己管理注册的广播接受者(不会管理静态注册的广播),而后做正常的分发,完全不涉及ActivityManagerService。因而比较高效。源码比较简单就不做分析。
欢迎关注我的Android进阶计划看更多干货。
参考文章
https://blog.csdn.net/chenweiaiyanyan/article/details/76907292
http://gityuan.com/2017/04/23/local_broadcast_manager/
https://www.songma.com/p/ca3d87a4cdf3
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 从源码理解BroadcastReceiver的工作过程