Android广播发送流程

Posted Zhang Jun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android广播发送流程相关的知识,希望对你有一定的参考价值。

大致流程图

广播发送

常见的通过ContextImpl.java发送广播的方法有下面几种
=> sendBroadcast/sendBroadcastAsUser :普通的广播发送,默认是当前userId,带有AsUser的是发送给特定的user
=> sendBroadcastMultiplePermissions/sendBroadcastAsUserMultiplePermissions :带有多个权限的广播发送
=> sendOrderedBroadcast/sendOrderedBroadcastAsUser :发送order有序的广播(order广播是一个接收完成下一个才能接收,接收者一个个按顺序接收)
=> sendStickyBroadcast/sendStickyBroadcastAsUser :发送粘性广播,粘性广播的意思是注册者注册了就马上能收到该类型(之前已经发送的粘性广播)的广播,接收者的注册不需要在发送者的前面
=> sendStickyOrderedBroadcast/sendStickyOrderedBroadcastAsUser :发送粘性而且是顺序order的广播

系统处理广播发送

  1. AMS接收广播的请求

    系统AMS通过broadcastIntentWithFeature接收广播的请求

  2. 修改增加默认flag解析可选广播参数BroadcastOptions

    • 如默认会增加 FLAG_EXCLUDE_STOPPED_PACKAGES ,不让stop(如通过forcestop会设置)的三方app接收静态广播
    • 根据是否粘性广播输出类似的日志:Broadcast (sticky) intent ordered=(true/false) userid=(userId)
    • 解析BroadcastOptions brOptions广播可选参数
  3. 保护广播isProtectedBroadcast、特定action的处理

    • 识别是否保护广播,这类广播不能给系统之外的app调用;而系统尽量发送保护广播,不然也会发出警告
    • 后台是否可以接收广播的识别(如果在system/etc/product/etc等地方加入allow-implicit-broadcast的广播则可以让后台接收,会加上FLAG_RECEIVER_INCLUDE_BACKGROUNDflag)
    • 特定action如包状态改变等的广播的处理
  4. 发送粘性广播的处理

    • 将粘性广播添加到AMS的mStickyBroadcasts(key是用户组,value是粘性广播列表stickies)中,单个用户组的粘性广播列表stickies(keyactionvalueintent)
    • 已经发送的粘性广播会放入AMS的mStickyBroadcasts中,后面动态注册的接收者就可以在注册的时候就接收这类粘性广播, 因为系统有保存这类广播
  5. 筛选出静态广播接受者

    • 这里主要是通过collectReceiverComponents来筛选出静态广播接受者
    • collectReceiverComponents:收集静态接收者的函数,其调用的是PMSqueryIntentReceivers进行查询,然后进行相应过滤。
      => 调用的是PMSqueryIntentReceivers进行查询
      => 判断是否只发送到单个用户FLAG_SYSTEM_USER_ONLY(多用户场景使用),如果是则进行过滤,只发送给单个用户
      => 如果带有broadcastAllowList(允许接收该广播uid的列表),则只让特定uid列表的静态接收者接收
    • PMS通过包安装信息获得静态接收者
      => 如果包发送给特定组件,则通过mComponentResolver的mReceivers.mActivities获取ParsedActivity,并最终得到对应的ActivityInfo
      => 如果没有指定特定组件和特定包,则通过mComponentResolver查询recevier,调用的是IntentResolverqueryIntent,根据各类Filter去查询,如mActionToFilter,查询到结果后在通过buildResolveList构建返回的结果List result
      => 如果是发送给特定包,则通过mPackages(安装的时候保存的,key是包名,valueandroidPackage/ParsingPackageImpl/PackageImpl)获取AndroidPackage(ParsingPackageImpl),通过ParsingPackageImpl得到这个包的receivers(pkg.getReceivers()),然后通过mComponentResolverqueryReceivers(ComponentResolver.java)->mReceivers.queryIntentForPackage->queryIntentFromList(IntentResolver.java)->buildResolveList
      此处queryIntentFromList只查询这个包的receivers(构建Pair<ParsedActivity, ParsedIntentInfo>来查询)
      在这里可以看到用来保存静态注册广播组件的mComponentResolvermReceivers.mActivitiesreceivers
  6. 筛选出动态广播接受者
    动态广播注册其实最终构建的是BroadcastFilter bf,并放入mReceiverResolver中去,而放入mReceiverResolver使用的方法是IntentResolveraddFilter

  7. order的平行广播的入队与分发
    如果没有设置ordered(也称之为serialized)按顺序,则针对动态注册类型的广播会一次性全部发送出去,于是我们可以看到类似这样的日志:Enqueueing parallel broadcas ***,这种无需等待APP的执行,很快系统就处理完了
    8. order广播接收者的过滤
    => 如果设置ordered(也称之为serialized)按顺序,不管是动态还是静态的注册者都是放入order的广播里面发送
    => 如果没有设置ordered,则只放入静态接收者。(普通的广播会有2个队列发送,一个是parallel broadcast,一个是ordered broadcast
    => 最终不管是静态还是动态注册的广播,只要是需要order发送的都会合并到receivers中,排序是按照优先级(android:order/IntentFilter.setPriority设置的优先级)

    广播发送时看到类似这样的日志:Enqueueing ordered broadcast ***,需要按顺序执行,前面一个接收者执行完成后才能让后面的继续执行,同时前面的可以中断后面接收者的执行

广播的拓展使用

一般广播的应用有3类:

  1. 针对高配置的设备:VIP广播,给一些关键的进程,关键的广播,放入特定的队列,不会由于本身广播队列阻塞了其广播分发,达到比较高效的分发
  2. 针对低配置的设备:
    => 延迟广播,间隔分发广播等。主要是广播如平行广播一次性发给所有进程,会导致系统负担;
    或者是发送给静态注册者需要启动应用,启动多个应用的时候会导致系统负载过重,可以将类似广播延迟分发。
    这个总负载还是一样的,只是分散开来,降低持续卡顿情况,至于偶发的峰值卡顿还是存在,需要别的方法配合,场景如开机、切换语言等
    => 限制广播:如在特定场景限制启动,限制接收等行为,降低系统负载。
    这类方法效果立竿见影,不过场景需要非常慎重,不然导致功能使用可能出现异常
    => 其它应用:特定场景修改超时时间,让用户感知的anr减少;针对用户不可见的anr,看一下是否需要用户感知等;接受者优先级调整等。
  3. 各类广播无法接收问题的解决等可以考虑动态打开一些基本的调试广播开关DEBUG_BROADCAST, DEBUG_BROADCAST_BACKGROUND, DEBUG_BROADCAST_DEFERRAL , DEBUG_BROADCAST_LIGHT, DEBUG_BACKGROUND_CHECK
  4. 目前有3个广播队列,一个是前台、一个是后台、一个是Offload的广播队列,其中前台广播超时时间是10s,后台是60sOffload的广播也是60s

以上是关于Android广播发送流程的主要内容,如果未能解决你的问题,请参考以下文章

android 广播自定义广播接收问题

Android 7.0 ActivityManagerService 广播(Broadcast)相关流程分析

BroadcastReceiver

Android08_广播接受者_服务

Android广播阻塞、延迟问题

12、注册广播有几种方式,这些方式有何优缺点?请谈谈Android引入广播机制的用意。