广播的流程

1. 注册广播

8.0对隐式广播的限制

隐式广播的定义:不针对特定应用的系统广播(比如ACTION_BOOT_COMPLETEDCONNECIVITY_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广播,参数如下:

类型serializedsticky
sendBroadcastfalsefalse
sendOrderedBroadcasttruefalse
sendStickyBroadcastfalsetrue
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统一进行派发,这样的坏处是由于涉及到进程间通信,效率会有所损失。如果我们只想在进程内进行广播发送,可以使用LocalBroadcastLocalBroadcast完全不涉及进程间通信

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】

  1. 开个循环,先处理并行广播 mParallelBroadcasts
  2. 处理完并行广播后,开始处理串行广播 ,BroadcastDispatcher作为串行广播分发器

【2】
按照步骤执行,通过binder异步机制,向receiver发送intent

BroadcastQueue.performReceiveLocked()
-> ActivityThread.ApplicationThread.scheduleRegisteredReceiver()
-> LoadedApk.ReceiverDispatcher.InnerReceiver.performReceive()
-> LoadedAPk.ReceiverDispatcher.performReceive()
-> LoadedAPk.ReceiverDispatcher.Args.getRunnable()
-> BroadcastReceiver.onReceive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值