• 广播的流程


    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()
    
  • 相关阅读:
    vue 概述
    Java设计模式——代理模式
    项目管理之常见七大问题挑战
    (附源码)springboot太原学院贫困生申请管理系统 毕业设计 101517
    Linux终端控制与ANSI转义序列
    IT入门知识第七部分《移动开发》(7/10)
    什么是域名备案?为什么要进行备案?备案后你将会获得下列益处
    Hadoop笔记-01概述
    uni-app学习笔记(二)
    Django序列化与反序列化
  • 原文地址:https://blog.csdn.net/qq_37776700/article/details/126965643