Android Broadcast广播机制分析

基于Android 6.0的源码剖析, 分析android广播的发送与接收流程。

一、概述

广播(Broadcast)机制用于进程/线程间通信,广播分为广播发送和广播接收两个过程,其中广播接收者BroadcastReceiver便是Android四大组件之一。

BroadcastReceiver分为两类:

  • 静态广播接收者:通过AndroidManifest.xml的标签来申明的BroadcastReceiver。
  • 动态广播接收者:通过AMS.registerReceiver()方式注册的BroadcastReceiver,动态注册更为灵活,可在不需要时通过unregisterReceiver()取消注册。

从广播发送方式可分为三类:

  • 普通广播:通过Context.sendBroadcast()发送,可并行处理
  • 有序广播:通过Context.sendOrderedBroadcast()发送,串行处理
  • Sticky广播:通过Context.sendStickyBroadcast()发送

二、注册广播

2.1 registerReceiver

广播注册,对于应用开发来说,往往是在Activity/Service中调用registerReceiver()方法,而Activity/Service都间接继承于Context抽象类,真正干活是交给ContextImpl类。另外调用getOuterContext()可获取最外层的调用者Activity/Service。

[ContextImpl.java]

当执行两参数的registerReceiver方法,增加两个broadcastPermission=null和scheduler=null调用四参数的注册方法。其中broadcastPermission拥有广播的权限控制,scheduler用于指定接收到广播时onRecive执行线程,当scheduler=null则默认代表在主线程中执行,这也是最常见的用法。 再然后调用6参数的registerReceiverInternal

2.2 registerReceiverInternal

[ContextImpl.java]

ActivityManagerNative.getDefault()返回的是ActivityManagerProxy对象,简称AMP,该方法中参数有mMainThread.getApplicationThread()返回的是ApplicationThread,这是Binder的Bn端,用于system_server进程与该进程的通信。

2.3 LoadedApk.getReceiverDispatcher

[-> LoadedApk.java]

不妨令 以BroadcastReceiver(广播接收者)为key,LoadedApk.ReceiverDispatcher(分发者)为value的ArrayMap 记为A。此处mReceivers是一个以Context为key,以A为value的ArrayMap。对于ReceiverDispatcher(广播分发者),当不存在时则创建一个。

2.3.1 创建ReceiverDispatcher

2.3.2 创建InnerReceiver

ReceiverDispatcher(广播分发者)有一个内部类InnerReceiver,该类继承于IIntentReceiver.Stub。显然,这是一个Binder服务端,广播分发者通过rd.getIIntentReceiver()可获取该Binder服务端对象InnerReceiver,用于Binder IPC通信。

2.4 AMP.registerReceiver

[-> ActivityManagerNative.java]

这里有两个Binder服务端对象callerreceiver,AMP通过Binder驱动将这些信息发送给system_server进程中的AMS对象,接下来进入AMS.registerReceiver。

2.5 AMS.registerReceiver

[-> ActivityManagerService.java]

其中mRegisteredReceivers记录着所有已注册的广播,以receiver IBinder为key, ReceiverList为value为HashMap。另外,这个过程涉及对象ReceiverListBroadcastFilterBroadcastRecord的创建。

在BroadcastQueue中有两个广播队列mParallelBroadcasts,mOrderedBroadcasts,数据类型都为ArrayList:

  • mParallelBroadcasts:并行广播队列,可以立刻执行,而无需等待另一个广播运行完成,该队列只允许动态已注册的广播,从而避免发生同时拉起大量进程来执行广播,前台的和后台的广播分别位于独立的队列。
  • mOrderedBroadcasts:有序广播队列,同一时间只允许执行一个广播,该队列顶部的广播便是活动广播,其他广播必须等待该广播结束才能运行,也是独立区别前台的和后台的广播。
2.5.1 AMS.getRecordForAppLocked

mLruProcesses数据类型为ArrayList<ProcessRecord>,而ProcessRecord对象有一个IApplicationThread字段,根据该字段查找出满足条件的ProcessRecord对象。

2.5.2 IntentFilter.match

该方法用于匹配发起的Intent数据是否匹配成功,匹配项共有4项action, type, data, category,任何一项匹配不成功都会失败。

小结

注册广播的过程,主要功能:

  • 创建ReceiverList(接收者队列),并添加到AMS.mRegisteredReceivers(已注册广播队列);
  • 创建BroadcastFilter(广播过滤者),并添加到AMS.mReceiverResolver(接收者的解析人);
  • 当注册的是Sticky广播,则创建BroadcastRecord,并添加到BroadcastQueue的mParallelBroadcasts(并行广播队列),注册后调用AMS来尽快处理该广播。

三、 发送广播

发送广播,同样往往是在Activity/Service中调用registerReceiver()方法,而Activity/Service都间接继承于Context抽象类,真正干活是交给ContextImpl类。

3.1 sendBroadcast

[ContextImpl.java]

3.2 AMP.broadcastIntent

[-> ActivityManagerNative.java]

3.3 AMS.broadcastIntent

[-> ActivityManagerService.java]

broadcastIntent()方法有两个布尔参数serialized和sticky来共同决定是普通广播,有序广播,还是Sticky广播,参数如下:

类型 serialized sticky
sendBroadcast false false
sendOrderedBroadcast true false
sendStickyBroadcast false true

3.4 AMS.broadcastIntentLocked

broadcastIntentLocked方法比较长,这里划分为8个部分来分别说明。

step1: 设置广播flag

这个过程最重要的工作是:

  • 添加flag=FLAG_EXCLUDE_STOPPED_PACKAGES,保证已停止app不会收到该广播;
  • 当系统还没有启动完成,则不允许启动新进程,,即只有动态注册receiver才能接受广播
  • 当非USER_ALL广播且当前用户并没有处于Running的情况下,除非是系统升级广播或者关机广播,否则直接返回。

BroadcastReceiver还有其他flag,位于Intent.java常量:

step2: 广播权限验证

主要功能:

  • 对于callingAppId为SYSTEM_UID,PHONE_UID,SHELL_UID,BLUETOOTH_UID,NFC_UID之一或者callingUid == 0时都畅通无阻;
  • 否则对于调用者进程为空并且不是persistent进程的情况下:
    • 当发送的是受保护广播mProtectedBroadcasts(只允许系统使用),则抛出异常;
    • 当action为ACTION_APPWIDGET_CONFIGURE时,虽然不希望该应用发送这种广播,处于兼容性考虑,限制该广播只允许发送给自己,否则抛出异常。
step3: 处理系统相关广播

这个过程代码较长,主要处于系统相关的广播,如下10个case:

step4:增加sticky广播

这个过程主要是将sticky广播增加到list,并放入mStickyBroadcasts里面。

step5:查询receivers和registeredReceivers

  • receivers:记录着匹配当前intent的所有静态注册广播接收者;
  • registeredReceivers:记录着匹配当前的所有动态注册的广播接收者。

其中,mReceiverResolver是AMS的成员变量,记录着已注册的广播接收者的resolver.

AMS.collectReceiverComponents

step6:处理并行广播

广播队列中有一个成员变量mParallelBroadcasts,类型为ArrayList,记录着所有的并行广播。

step7:合并registeredReceivers到receivers

step8: 处理串行广播

广播队列中有一个成员变量mOrderedBroadcasts,类型为ArrayList,记录着所有的有序广播。

3.5 小结

  • 注册广播的小节[2.4]阶段, 会处理Sticky广播;
  • 发送广播的[step 6]阶段, 会处理并行广播;
  • 发送广播的[step 8]阶段, 会处理串行广播;

上述3个处理过程都是通过调用scheduleBroadcastsLocked()方法来完成的,接下来再来看看这个方法.

四、 处理广播

在发送广播过程中会执行scheduleBroadcastsLocked方法来处理相关的广播

4.1 scheduleBroadcastsLocked

[-> BroadcastQueue.java]

在BroadcastQueue对象创建时,mHandler=new BroadcastHandler(handler.getLooper());那么此处交由mHandler的handleMessage来处理:

4.2 processNextBroadcast

[-> BroadcastQueue.java]

此处mService为AMS,整个流程还是比较长的,全程持有AMS锁,所以广播效率低的情况下,直接会严重影响这个手机的性能与流畅度,这里应该考虑细化同步锁的粒度。

step1: 处理并行广播

step2: 处理当前有序广播

mTimeoutPeriod,对于前台广播则为10s,对于后台广播则为60s。广播超时为2*mTimeoutPeriod*numReceivers,接收者个数numReceivers越多则广播超时总时长越大。

step3: 获取下条有序广播

step4: 处理下条有序广播

  • 如果是动态广播接收者,则调用deliverToRegisteredReceiverLocked处理;
  • 如果是静态广播接收者,且对应进程已经创建,则调用processCurBroadcastLocked处理;
  • 如果是静态广播接收者,且对应进程尚未创建,则调用startProcessLocked创建进程。

4.3 deliverToRegisteredReceiverLocked

[-> BroadcastQueue.java]

4.4 deliverToRegisteredReceiverLocked

[-> BroadcastQueue.java]

4.5 ATP.scheduleRegisteredReceiver

ATP位于system_server进程,是Binder Bp端通过Binder驱动向Binder Bn端发送消息, ATP所对应的Bn端位于发送广播调用端所在进程的ApplicationThread,即进入AT.scheduleRegisteredReceiver, 接下来说明该方法。

4.6 AT.scheduleRegisteredReceiver

[-> ActivityThread.java]

此处receiver是注册广播时创建的,见小节[2.3],可知该receiver=LoadedApk.ReceiverDispatcher.InnerReceiver

4.7 InnerReceiver.performReceive

[-> LoadedApk.java]

4.8 ReceiverDispatcher.performReceive

[-> LoadedApk.java]

其中Args继承于BroadcastReceiver.PendingResult,实现了接口Runnable。这里mActivityThread.post(args) 消息机制,关于Handler消息机制,见Android消息机制1-Handler(Java层),把消息放入MessageQueue,再调用Args的run()方法。

4.9 Args.run

[-> LoadedApk.java]

最终调用BroadcastReceiver具体实现类的onReceive()方法。

4.10 PendingResult.finish

[-> BroadcastReceiver.java]

此处AMP.finishReceiver,经过binder调用,进入AMS.finishReceiver方法

4.11 AMS.finishReceiver

五、总结

未完留坑,后续总结以及增加流程图说明…


附录

本文所涉及的源码:

作者微博:@Gityuan

原文出处:Gityuan.com

1 2 收藏 评论

关于作者:gityuan

Android系统工程师,www.gityuan.com博主,个人新浪微博:http://weibo.com/gityuan 个人主页 · 我的文章 · 3 ·     

可能感兴趣的话题



直接登录
跳到底部
返回顶部