Android系统广播(2)--AMS端发送广播

Android系统广播(2)–AMS端发送广播

注册完成广播接收器,接下来就是等待广播的发送。开发者可以在Service、Activity中使用sendBroadcast()发送广播。广播发送也是在ContextImpl中实现,直接调用到AMS的broadcastIntent()。如下图,这里也是以此开始。

《Android系统广播(2)--AMS端发送广播》

客户端的发送广播所有接口都统一到AMS的broadcastIntent()函数。

一、在AMS的broadcastIntent()函数中:

1.首先调用verifyBroadcastLocked()检测传入intent合法性,不能携带文件描述符等。

2.根据传入的IApplicationThread获取发送广播的客户端进程对应的ProcessRecord。

3.获取到相应数据结构后开始调用broadcastIntentLocked()执行具体逻辑。

二、在broadcastIntentLocked()内具体发送广播逻辑

1.首先为传入的intent添加FLAG_EXCLUDE_STOPPED_PACKAGES标识。这个标识代表应用第一次安装但未使用过,或从程序管理器被强行关闭后,将处于停止状态。对所有广播添加该标识,如果其对应进程处于停止状态,讲不会收到广播。

2.获取各种id,对特殊广播Action处理及其权限检查等。

3.判断如果发送的广播是粘性广播,查找处理替换为最新

  • 首先检查对应客户端进程是否存在发送粘性广播的权限。发送粘性广播不能指定接受者权限,同时粘性广播需要指定组件信息等。检查如果是发送给指定user的粘性广播是否与发送给所有user的冲突。

  • 从mStickyBroadcasts中根据userId取出其之前已经发送对应的粘性广播。根据待发送intent的action获取已经存在并发送过的粘性广播intent。接着for循环所有获取到的已经存在并发送的粘性广播替换其为最新广播intent。

4.获取待发送广播intent匹配到的接收器。到这里需要说明两个变量声明及其赋值:

  • List receivers:保存匹配到当前广播的静态注册的广播接收器,如果当前是有序广播,还会插入动态注册的广播接收器。

  • List registeredReceivers:保存匹配当前广播的动态注册的接收器。

  • 检查广播intent是否设置FLAG_RECEIVER_REGISTERED_ONLY,设置该标识意味只会将这个广播发送动态注册接收器。这里判断如果没有设置会使用collectReceiverComponents()方法从PKMS中根据intent获取满足条件的静态广播接收器赋值于receivers。

  • 检查传入intent是否指明Component,没有指明该信息调用mReceiverResolver.queryIntent()查询满足接受条件的动态注册的接收器赋值于registeredReceivers。

  • 检查是否设置标识FLAG_RECEIVER_REPLACE_PENDING,设置该标识标识新的广播可以替换还未处理的AMS广播队列中已存在的广播。

5.处理动态注册的广播接收器对应的广播

  • 首先处理无序动态注册的广播接收器。调用broadcastQueueForIntent()方法根据intent的flag获取一个待发送广播的队列。所有待发送广播都置于队列之中。

  • 新建对象BroadcastRecord分装数据,传入待接收广播接收器registeredReceivers。

  • 如果设置了FLAG_RECEIVER_REPLACE_PENDING调用queue.replaceParallelBroadcastLocked()执行替换操作。如果替换成功则不做处理,如果没执行替换操作则queue.enqueueParallelBroadcastLocked(r)将其放入发送队列然后queue.scheduleBroadcastsLocked()触发广播发送流程。这两个函数实现稍后看。

6.接着开始处理静态注册或者是有序动态的receivers。

  • 首先处理特殊的Action,当发送广播Action为ACTION_PACKAGE_ADDED等时,需要剔除刚刚安装并注册了该接收器的应用。系统不希望应用刚刚安装就可以接受到广播然后启动。

  • 接下来需要进行有序静态动态接收器的合并。

    • NT是静态注册接收器的数量,NR是动态注册接收器的数量。while (it < NT && ir < NR) 这个while循环满足的条件是:发送的有序广播,并且该广播对应一定数目的静态动态接收器。循环里面的工作是分别取出所有的静态与动态接收器,根据优先级最后将动态有序的广播接收器插入到receivers中。

    • while (ir < NR)这个循环是处理插入剩下的动态接收器。至此receivers已经是一个根据优先级存放接收器信息的list。

  • 如果receivers存在接收器。调用broadcastQueueForIntent()方法根据intent的flag获取一个待发送广播的队列。所有待发送广播都置于队列之中。

  • 新建对象BroadcastRecord分装数据,传入待接收广播接收器registeredReceivers。

  • 如果设置了FLAG_RECEIVER_REPLACE_PENDING调用queue.replaceParallelBroadcastLocked()执行替换操作。如果替换成功则不做处理,如果没执行替换操作则queue.enqueueParallelBroadcastLocked(r)将其放入发送队列然后queue.scheduleBroadcastsLocked()触发广播发送流程。这两个函数实现稍后看。

7.处理完以上逻辑则返回ActivityManager.BROADCAST_SUCCESS。

三、在BroadcastQueue中执行processNextBroadcast(true)具体发送逻辑

如前所述,发送广播需要调用BroadcastQueue两个方法。enqueueOrderedBroadcastLocked() / enqueueParallelBroadcastLocked() 将要发送的BroadcastRecord放入指定队列等待。scheduleBroadcastsLocked()触发发送流程,该函数发送异步消息执行processNextBroadcast(true)实现具体逻辑。

1.先处理无序动态注册普通广播接收器

  • 在while循环中逐个处理,条件mParallelBroadcasts非空,mParallelBroadcasts内部存放无序动态注册广播接收器。

  • 取出待处理的BroadcastRecord获取该广播需要发送给的接收器数目,for循环获取接收器对应的BroadcastFilter依次执行deliverToRegisteredReceiverLocked()执行分发,该函数稍后看。

  • 然后调用addBroadcastToHistoryLocked(r)将这个广播记录加入历史记录中。

2.开始处理有序动态或者静态广播。

对于有序或静态广播而言,需要依次向每个BroadcastReceiver发送广播,前一个处理完毕后才能发送下一个广播。如果BroadcastReceiver对应的进程还未启动,则需要等待。

  • 首先处理因为进程还未启动处于等待状态的BroadcastRecord。

    • mPendingBroadcast就是用于保存因为对应进程还未启动,而处于等待状态的BroadcastRecord。

    • mPendingBroadcast不为空存在等待的广播。首先获取广播接收器对应进程ProcessRecord,如果该进程存在,对应进程启动成功后会自动发送需要的广播,则重置mPendingBroadcast状态闲置。如果该进程不存在则这里直接需要返回。等待进程启动成功后处理对应广播。

注意:有序和静态广播必须依次处理 因此,若前一个BroadcastRecord对应的某个进程启动较慢,不仅会影响该BroadcastRecord中后续进程接收广播还会影响到后续所有BroadcastRecord对应进程接收广播。

  • do循环处理之前发送BroadcastRecord的状态。循环条件是获取的BroadcastRecord为空。当不满足该条件跳出循环时代表获取了一个待发送的BroadcastRecord。

    • mOrderedBroadcasts存放需要处理的动态有序或者静态接收器。

    • 如果mOrderedBroadcasts大小为0则当前不存在需要处理的广播直接返回。否则获取当前第一个待处理BroadcastRecord。

    • 判断获取的BroadcastRecord是否已经超时,超时强制结束赋值IDLE状态。

    • 判断获取的BroadcastRecord状态如果不是IDLE,则代表正在处理中,直接返回。

    • 接着判断该BroadcastRecord不存在接收器、或者已经处理完毕、或者强制取消,此时将该广播的处理结果发送给resultTo对应BroadcastReceiver。然后cancelBroadcastTimeoutLocked()取消广播定时,addBroadcastToHistoryLocked(r);添加处理记录。最后移除这个广播mOrderedBroadcasts.remove(0);。如果不满足该条件继续循环。

  • 上述循环结束选出了待发送BroadcastRecord,得到下一个接收器下标、为该广播处理设置超时时间、获取接收器对象nextReceiver。

  • 接下来根据nextReceiver类型发送广播给接收器

    • 如果是动态注册的有序广播,调用deliverToRegisteredReceiverLocked()分发该广播。如果发送该广播出错则重新执行scheduleBroadcastsLocked()尝试发送。执行到这里AMS已经通知了接收器,只需要等待接收器处理结果这里就直接返回。

    • 如果是静态注册的广播接收器,首先对组件信息、广播权限等检查。获取该接收器对应进程的ProcessRecord,如果进程存在,则直接执行processCurBroadcastLocked(r, app);分发广播并返回。如果进程不存在启动进程并判断结果,进程启动失败则强制停止结束本次发送继续后续并返回。最后因为新启进程保存新进程需要启动的BroadcastRecord记录到mPendingBroadcast,进程启动成功后根据这个对象接着处理广播发送。

上述步骤中,在发送广播时有两个重要函数:deliverToRegisteredReceiverLocked()分发给动态广播接收器,processCurBroadcastLocked()分发给静态广播接收器。

四、分发给静态动态接收器时候的

1.首先介绍deliverToRegisteredReceiverLocked()函数分发给动态广播接收器。

  • 首先一系列权限检查,通过后调用performReceiveLocked()执行发送。

    • 在该performReceiveLocked()函数中如果存在客户端进程,则使用跨进程调用客户端app.thread.scheduleRegisteredReceiver(),这种方式确保正确顺序调用,一般来说动态注册的接收器对应进程一定存在。如果不存在则直接跨进程调用对应接收器的receiver.performReceive()方法分发。
  • 跨进程调用结束后如果是有序广播重新赋值广播状态。

2.接着processCurBroadcastLocked()分发给静态广播接收器。

  • 赋值变量及调整对应进程优先级。静态广播可以直接发送代表对应进程一定存在。

  • 设置广播intent对应的接收器组件信息。

  • 最后直接回调对应客户端进程app.thread.scheduleReceiver()方法进行广播分发。

五、静态广播接收器进程新启动处理待发送广播

如果在发送给静态接收器时其对应进程不存在,则需要启动新进程成功后接着发送广播给对应的静态接收器。

1.新进程启动后,会回调AMS的attachApplication()函数通知AMS,在该函数中会执行待发送广播给指定静态接收器。

  • 在attachApplication()函数中会首先获取对应进程的ProcessRecord,接着一系列检查。

  • 检查通过,使用sendPendingBroadcastsLocked()检查是否存在需要发送的静态广播,判断依据就是之前赋值的变量mPendingBroadcast。

  • 如果存在需要发送的静态广播调用sendPendingBroadcastsLocked()函数。

    • 该函数内部遍历当前广播队列,分别调用其queue.sendPendingBroadcastsLocked()函数。

    • 在BroadcastQueue.sendPendingBroadcastsLocked()函数中将需要发送的mPendingBroadcast赋值,然后调用processCurBroadcastLocked()发送给指定的接收器。发送成功返回结果。

    原文作者:leif_123
    原文地址: https://blog.csdn.net/leif_/article/details/78709556
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞