上一篇分析了Android系统中广播的注册和注销,这一篇我们继续分析发送广播的源码流程,广播的发送相对来说比较复杂,所以需要比较长的时间来看,如果你看懂了流程相对来说再研究流程中的细节就比较简单了。
先看一张时序图,因为里面涉及到循环过程,代码中会提到,但是时序图没有绘制,所以需要注意一下。
0、ContextImpl.sendBroadcast
1 | /** |
之前我们讲过调用ActivityManagerProxy的broadcastIntent方法,然后通过Binder调用ActivityManagerService中的对应方法。
1、ActivityManagerProxy.broadcastIntent
1 | public int broadcastIntent(IApplicationThread caller, |
2、ActivityManagerService.broadcastIntent
1 | // 发送广播 |
3、ActivityManagerService.broadcastIntentLocked
1 | final int broadcastIntentLocked(ProcessRecord callerApp, |
这里代码比较多,简单解释一下,首先是判断是不是系统广播,也就是switch语句中的部分,这部分的广播是系统发出的,根据不同广播做出不同的处理,系统广播我们可以接收但是不能发送,只能由系统发出,详细的不再解释,自己可以看看;然后是sticky广播的处理;然后是静态广播和动态广播的处理,在动态广播和静态广播处理中,先处理动态广播接收者,再处理静态广播接收者,因此动态广播接收者先收到广播,然后是静态广播接收者,动态广播和静态广播接收者都会通过调用BroadcastQueue.scheduleBroadcastsLocked方法来发送广播,在看这个代码之前先看一下时序图中的第四步。
4、AMS.broadcastQueueForIntent
1 | // 根据Intent所带标签判断接收者是以前台优先级还是后台优先级运行,前台接收者优先级超时时间较短 |
这个方法主要是根据Intent中的标签在判断是接收者是后台优先级还是前台优先级,关于这两个概念看上面代码中注释的内容。
5、BroadcastQueue.scheduleBroadcastsLocked
1 | // 驱动广播,所有广播都应该从这里走,然后会到processNextBroadcast |
广播中大多数内容的处理是在BroadcastQueue类中,上面有个mBroadcastsScheduled参数,如果为ture则阻止继续执行,那么我们看到下面发完消息后会将其设置为true,我们接着看哪里将其执行为false的,
6、BroadcastQueue.processNextBroadcast
1 | // 广播的核心部分,参数fromMsg是用来描述AMS类的成员函数processNextBroadcast是否是用来处理类型为 |
我们根据第五步的发送消息类型可以跟踪到,消息执行是在BroadcastHandler的handleMessage方法中调用的,这里出入一个参数fromMsg,也就是该广播是不是通过Handler发送消息的方式传递的,那么我们上面知道我们这就是通过这种方式执行的,所以在代码中会将mBroadcastsScheduled参数设置为false,也就是这个消息开始执行了则开始运行下一个消息发送。还有就是通过直接调用的方式来执行的,那么就不会改变这个参数。
接着看,先执行无序广播,无序广播接收者不需要等待,所以直接for循环执行,调用deliverToRegisteredReceiverLocked方法发送广播。
7、deliverToRegisteredReceiverLocked
1 | // AMS将一个广播发给一个目标广播接收者之前,有可能需要检查这个广播的发送者和接收者的权限。这个权限检查是 |
我们这里不是有序广播,正常也不会备份,所以会走else里面的方法performReceiveLocked。如果是有序广播我们后面再分析
8、performReceiveLocked
1 | /** |
如果进程存在,则执行ActivityThread.scheduleRegisteredReceiver方法,否则直接调用receiver.performReceive方法发送广播。因为我们动态注册的所以会执行第一种情况。
9、ActivityThread.scheduleRegisteredReceiver
1 | // 处理非串行化动态广播,非串化ordered是false,这里的receiver对应的是 |
这里我们看到和上面一样最终还是执行receiver.performReceive方法来发送广播。
10、LoadedApk.ReceiverDispatcher.InnerReceiver.performReceive
1 |
|
我们上一章注册广播是讲过LoadedApk.ReceiverDispatcher对象封装了广播接收者,如果该广播接收者注册了,那么该对象就会存在,则会调用LodedApk.ReceiverDispatcher.performReceive,否则调用AMS.finishReceiver方法,我们先看有广播的情况。
11、LodedApk.ReceiverDispatcher.performReceive
1 | public void performReceive(Intent intent, int resultCode, String data, |
上面初始化了一个Args对象,该对象实现了Runnable接口,在if语句中调用post方法,会调用Args中的run方法。
13、Args.run
1 | public void run() { |
在这里调用BroadcastReceiver.onReceive方法,这样就会执行完一次无需广播发送过程。我们再回到第10步,如果对象接收者不存在则调用AMS.finishReceiver
15、AMS.finishReceiver
1 | public void finishReceiver(IBinder who, int resultCode, String resultData, |
这里是如果发送广播时接收者不存在,那么要完成该次广播,并且判断是否执行发送给下一个广播接收者,如果需要发送给下个广播接收者要再次调用BroadcastQueue.processNextBroadcast方法。这样就又回到了前面第六步。我们再往前看BroadcastQueue.processNextBroadcast方法,执行完无序广播后开始执行有序广播,因为有序广播是一个执行完再执行下一个所以必须设置超时,并且如果超时要立即接受广播,
17、broadcastTimeoutLocked
1 | /** |
这里主要是超时的处理,因为注释已经非常详细,所以不再解释,所以我们在写广播接收到消息是最后采用Handler或者启动服务将消息处理的过程放到Handler中或者服务中,而不是直接在广播接收者中完成。
18、setBroadcastTimeoutLocked
1 | // 设置超时时间 |
如果广播还没有超时就执行完了就从新设置下一个广播的起始时间,方便计算下一个广播的超时。这个消息处理就不分析了,自己看一下。再回到第六步BroadcastQueue.processNextBroadcast方法中,如果广播发送完成,或者被中断或者取消,则判断是否发送最后的广播,如果要发送,则执行performReceiveLocked方法,这个方法在第八步讲了,这里就不再重复分析。我们知道广播有静态广播接收者和动态广播接收者,动态广播接收者进程都是启动的,但是静态的就不一定了,可能进程还没有启动,就想第六步中代码注释分析的一样,我们上面处理的都是动态注册的,不需要判断进程是否已经启动,但是静态广播就需要判断该进程是否已经启动了。这里的ResolveInfo表示静态广播接收者对象。找到对象后判断是否要跳过该广播,如果跳过则通过执行scheduleBroadcastsLocked方法发送消息执行下一个广播。如果不需要跳过该广播时,判断该进程是否存在,如果该静态广播的进程已经存在了则执行processCurBroadcastLocked,将广播发送给该进程处理,如果不存在则启动进程,并且将广播对象BroadcastRecord放入等待广播列表中,如果启动失败则接受该广播并判断是否执行下一个广播,我们先将一下进程存在时广播的处理,最后再分析进程启动时的广播处理。
21、processCurBroadcastLocked
1 | // 这里只是将广播发送到接收者进程,需要一直等待接收者进程处理完广播后返回,AMS才能处理当前BroadcastRecord |
调用scheduleReceiver方法处理广播。
22、ApplicationThread.scheduleReceiver
1 | // 处理应用进程中接收到的静态广播消息,实际处理该广播的是ActivityThread.handleReceiver函数 |
通过Handler消息处理机制调用handleReceiver方法处理该静态广播
23、ApplicationThread.handleReceiver
1 | // 主要包括三步: |
上面最开始注释写了这里分为三步,创建BroadcastReceiver对象,执行onReceive函数,结束。这样进程存在时静态广播就发送完成了。最后我们分析需要启动进程时发送广播的流程,我们先看一张时序图。
在Android系统源码分析–Process启动过程一章我们分析了进程启动过程,在最后会调用ActivityThread.mian方法,我们从这个方法开始看:
0、ActivityThread.mian
1 | /** |
这里会初始化一个ActivityThread对象,然后调用该对象的attach方法,传入参数false。
1、ActivityThread.attach
1 | private void attach(boolean system) { |
我们上面接受了传入参数是false,则会走if中的代码,也就是会执行mgr.attachApplication方法,通过注释我们知道会调用ActivityManagerProxy.attachApplication方法,然后传递到ActivityManagerService.attachApplication方法。
2、ActivityManagerProxy.attachApplication
1 | public void attachApplication(IApplicationThread app) throws RemoteException { |
3、ActivityManagerService.attachApplication
1 | // 用来处理ActivityMangerProxy传递过来的类型为ATTACH_APPLICATION_TRANSACTION的进程间通信请求 |
这里调用attachApplicationLocked方法:
4、attachApplicationLocked
1 | private final boolean attachApplicationLocked(IApplicationThread thread, |
因为我们这里分析发送广播,所以我们只关心广播的相关处理,所以上面只保留了广播的内容,if语句中判断当前应用没有问题,并且有等待的广播,才会调用sendPendingBroadcastsLocked方法,前面我们知道静态广播有一种是进程不存在的,所以这个广播就要放到等待广播中,这里就开始处理等待广播的情况。
5、sendPendingBroadcastsLocked
1 | // The app just attached; send any pending broadcasts that it should receive |
mBroadcastQueues是包含前台优先级和后台优先级的广播队列,这里分别调用前台和后台优先级广播的BroadcastQueue.sendPendingBroadcastsLocked方法。
6、BroadcastQueue.sendPendingBroadcastsLocked
1 | // 未启动进程的广播接收者需要先启动进程,最后到达这个函数 |
这里是找到等待处理的广播并且判断是否为空,并且是否和当前进程的pid相同,也就是是不是找个进程的等待广播,如果是就调用processCurBroadcastLocked方法进行处理,这个方法在上面第21步中已经讲过,所以又回到了进程存在的情况下广播的处理。这样整个广播的处理就分析完了,代码量很大,但是逻辑很清楚,只需要对着注释多看看看就明白了。下一篇我们开始讲Activity启动的源码分析。
为了方便记忆,这里添加一个发送广播的流程图,这样对照着上面的代码流程再看就会好理解很多。
代码地址:
直接拉取导入开发工具(Intellij idea或者Android studio)
注
Android开发群:192508518
微信公众账号:Code-MX
注:本文原创,转载请注明出处,多谢。