1. 注册广播
8.0对隐式广播的限制
隐式广播的定义:不针对特定应用的系统广播(比如ACTION_BOOT_COMPLETED
和CONNECIVITY_ACTION
)和由App发送的不指定包名的全局广播
由于8.0之前隐式广播可以通过静态的方式注册,导致一些系统广播发出后引起大量App进程被拉起,严重影响了系统性能。8.0开始Android禁用了隐式广播
BroadcastRecord
framework/base/services/core/java/com/android/server/am
- BroadcastRecord
是一个远程Binder;
Binder本身来作为Server中提供的节点,client拿到Binder实体对象对应的地址去访问Server;
该类记录了一些变量:callerApp、callerPackage、anrCount…
ContextImpl
第一步还是来到ContextImpl
frameworks/base/core/java/android/app
ContextImpl
- Intent registerReceiverInternal()
LoadedApk
第二步来到LoadedApk
frameworks/base/core/java/android/app
LoadedApk
- IIntentReceiver getReceiverDispatcher()
-
ReceiverDispatcher:维护 IIntentReceiver,也就是 InnerReceiver;
-
InnerReceiver:是一个远程Binder,作为 AMS registerReceiverWithFeature() 参数的一员;
final static class InnerReceiver extends IIntentReceiver.Stub{}
IIntentReceiver.Stub 除了是 Binder 之外,还实现了 IIntentReceiver;
ActivityManagerService
第三步来到AMS,默认情况下是后台广播
public Intent registerReceiverWithFeature(..){
...
// Enqueue broadcasts for all existing stickies that match
// this filter.
if (allSticky != null) {
ArrayList receivers = new ArrayList();
receivers.add(bf);
final int stickyCount = allSticky.size();
for (int i = 0; i < stickyCount; i++) {
Intent intent = allSticky.get(i);
// 根据intent返回前台或后台广播队列
BroadcastQueue queue = broadcastQueueForIntent(intent);
// 创建BroadcastRecord
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, null, -1, -1, false, null, null, null, OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1, false, null,
false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
// 该广播加入到并行广播队列
queue.enqueueParallelBroadcastLocked(r);
// 调度广播,发送BROADCAST_INTENT_MSG消息,触发处理下一个广播
queue.scheduleBroadcastsLocked();
}
return sticky;
}
2. 获取ContextImpl实例
该类不是 public 类,外部是获取不到的;
查看主线程代码,可以知道 Activity、Service 的 Context 实例具体都是 ContextImpl;
可以通过反射进行获取,反射方法为 getImpl,详情可查看链接中示例代码;
static ContextImpl getImpl(Context context) {
Context nextContext;
while ((context instanceof ContextWrapper) &&
(nextContext=((ContextWrapper)context).getBaseContext()) != null) {
context = nextContext;
}
return (ContextImpl)context;
}
3. 发送广播
broadcastIntent()方法有两个布尔参数serialized和sticky来共同决定是普通广播,有序广播,还是Sticky广播,参数如下:
类型 | serialized | sticky |
---|---|---|
sendBroadcast | false | false |
sendOrderedBroadcast | true | false |
sendStickyBroadcast | false | true |
ContextImpl.sendBroadcast()
-> AMS.broadcastIntentWithFeature()
-> broadcastIntentLocked()
step1:设置flag
step2:广播权限验证
step3:处理系统相关广播
step4: 增加sticky广播
step5: 查询receivers和registeredReceivers
step6: 处理并行广播
step7: 合并registeredReceivers到receivers
step8: 处理串行广播
step1:增加flag,不会发送给已停止的package
step2:不允许发送给受保护的广播
step5:查询静态与非静态注册广播
step6:动态注册且非serialized广播
广播发送方式可以分为三类
- 普通广播:通过Context.sendBroadcast()发送,可并行处理;
- 有序广播:通过Context.sendOrderedBroadcast()发送,串行处理;
- Sticky广播:通过Context.sendStickyBroadcast()发送;
ContextImpl.sendBroadcast(),一般用的最多的也是这个;
AMS.broadcastIntentWithFeature()方法;
注意事项:
- 广播的发送是异步的,也就是说调用
sendBroadcast
后函数立即返回,不关心接收方是否接收到 - 广播的接收回调函数
onReceive
是在主线程执行 - 广播基于binder机制,所以传递数据大小受binder的数据缓冲区大小限制,也就是1M
LocalBroadcast
普通广播是一定要走AMS的,由AMS统一进行派发,这样的坏处是由于涉及到进程间通信,效率会有所损失。如果我们只想在进程内进行广播发送,可以使用LocalBroadcast
,LocalBroadcast
完全不涉及进程间通信
4. 处理广播
framework/base/services/core/java/com/android/server/am
AMS.registerReceiverWithFeature() // 注册广播
-> BroadcastQueue.scheduleBroadcastsLocked()// 处理广播
BroadcastQueue
-> scheduleBroadcastsLocked() // BROADCAST_INTENT_MSG
-> BroadcastQueue.BroadcastHandler.handleMessage()
-> processNextBroadcast()
-> processNextBroadcastLocked()【1】
-> performReceiveLocked()【2】 // 处理广播消息的消息,调用onReceive()
-> cancelBroadcastTimeoutLocked() // 拆炸弹
-> setBroadcastTimeoutLocked() // 埋炸弹
【1】
- 开个循环,先处理并行广播 mParallelBroadcasts
- 处理完并行广播后,开始处理串行广播 ,BroadcastDispatcher作为串行广播分发器
【2】
按照步骤执行,通过binder异步机制,向receiver发送intent
BroadcastQueue.performReceiveLocked()
-> ActivityThread.ApplicationThread.scheduleRegisteredReceiver()
-> LoadedApk.ReceiverDispatcher.InnerReceiver.performReceive()
-> LoadedAPk.ReceiverDispatcher.performReceive()
-> LoadedAPk.ReceiverDispatcher.Args.getRunnable()
-> BroadcastReceiver.onReceive()