广播是怎么注册的呢?

0

阶段

组件/数据结构

作用描述

存储位置/关联关系

App进程阶段

BroadcastReceiver

开发者自定义的广播接收器,实现onReceive

方法处理事件。

App进程(Activity/Service等组件内)

ReceiverDispatcher

将BroadcastReceiver封装为跨进程可调用的

IIntentReceiver接口对象。

LoadedApk中创建,关联InnerReceiver

InnerReceiver

继承IIntentReceiver.Stub的Binder实体,AMS通过它回调App主线程的onReceive

作为Binder服务端存在于App进程,代理对象传递至AMS

SystemServer进程阶段

ReceiverList

以InnerReceiver的Binder为Key,管理同一接收器的多个BroadcastFilter

存储在AMS的mRegisteredReceivers(HashMap)

BroadcastFilter

关联IntentFilter与ReceiverList,描述接收器感兴趣的广播类型。

注册到mReceiverResolver(全局匹配引擎)和ReceiverList

BroadcastQueue

管理待分发的广播,分为有序和无序队列。

AMS中维护,异步分发广播

核心数据结构

mRegisteredReceivers

维护所有动态注册的ReceiverList,避免重复注册。

AMS内存(HashMap结构)

mReceiverResolver

基于IntentFilter的快速匹配引擎,优化广播分发效率。

AMS内存(IntentResolver子类)

mStickyBroadcasts

缓存粘性广播,按用户ID和Action分类存储。

AMS内存(SparseArray结构),供新注册接收器匹配历史广播

源码流程:​

app.registerReceiver()->Context.registerReceiver(BroadcastReceiver, IntentFilter)#Context.registerReceiver(BroadcastReceiver, IntentFilter)->Context.registerReceiverInternal()##Context.registerReceiverInternal()->LoadedApk.getReceiverDispatcher:返回一个为IIntentReceiver类型的ReceiverDispatcher(广播快递员)###LoadedApk.getReceiverDispatcher->ReceiverDispatcher实例化####ReceiverDispatcher实例化->InnerReceiver实例化:可以看到 InnerReceiver extends IIntentReceiver.Stub 是跨进程对象的一个实体##Context.registerReceiverInternal()->ActivityManagerService.registerReceiverWithFeature(IIntentReceiver)###ActivityManagerService.registerReceiverWithFeature(IIntentReceiver)->ActivityManagerService.registerReceiverWithFeatureTraced####ActivityManagerService.registerReceiverWithFeatureTraced->RegisteredReceivers.get(receiver.asBinder())创建ReceiverList,并mRegisteredReceivers全局注册表记录####ActivityManagerService.registerReceiverWithFeatureTraced->new BroadcastFilter:创建BroadcastFilter并注册到Resolver,并mReceiverResolver注册到全局过滤器####ActivityManagerService.registerReceiverWithFeatureTraced->new BroadcastRecord创建广播记录并加入队列,并mBroadcastQueue.enqueueBroadcastLocked(r)异步分发

源码重点函数分析:

注册广播接收器

registerReceiver来时往里面看,因为Activity是Context的子类,这个注册的方法的实现则是在ContextImpl当中,其中最终调用的方法为registerReceiverInternal,代码如下​​​

/** * 动态注册广播接收器的核心方法,完成跨进程通信的Binder封装和AMS注册 * @param receiver 开发者定义的广播接收器实例 * @param userId 用户ID(多用户支持) * @param filter 意图过滤器,描述接收器感兴趣的广播类型 * @param broadcastPermission 接收广播所需的权限(可选) * @param scheduler 指定回调线程的Handler(null则使用主线程Handler) * @param context 关联的Context对象 * @param flags 注册标志位 * @return 匹配的粘性广播Intent(若无则返回null) */private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,        IntentFilter filter, String broadcastPermission,        Handler scheduler, Context context, int flags) {    // 1. 创建跨进程通信对象IIntentReceiver    IIntentReceiver rd = null;    if (receiver != null) {        if (mPackageInfo != null && context != null) {            // 1.1 使用主线程Handler(若未指定)            if (scheduler == null) {                scheduler = mMainThread.getHandler(); // 获取ActivityThread的H主线程Handler[1,3](@ref)            }            // 1.2 通过LoadedApk获取ReceiverDispatcher(封装跨进程Binder对象)            rd = mPackageInfo.getReceiverDispatcher(                receiver, context, scheduler,                mMainThread.getInstrumentation(), true); // true表示需要注册[1,5](@ref)        } else {            // 1.3 备用路径:直接创建ReceiverDispatcher(非标准场景)            if (scheduler == null) {                scheduler = mMainThread.getHandler();            }            rd = new LoadedApk.ReceiverDispatcher(mMainThread.getApplicationThread(),                    receiver, context, scheduler, null, true).getIIntentReceiver();        }    }    try {        // 2. 跨进程调用AMS注册广播        final Intent intent = ActivityManager.getService().registerReceiverWithFeature(                mMainThread.getApplicationThread(), // 应用主线程的IApplicationThread对象                mBasePackageName,                   // 包名标识                getAttributionTag(),                // 归因标签(用于权限跟踪)                AppOpsManager.toReceiverId(receiver), // 接收器唯一ID                rd,                                // 跨进程回调接口                filter,                            // 意图过滤器                broadcastPermission,               // 所需权限                userId,                            // 用户ID                flags);                            // 标志位[5](@ref)        // 3. 处理返回的粘性广播(若存在)        if (intent != null) {            intent.setExtrasClassLoader(getClassLoader());            // 3.1 准备Intent数据安全传输到应用进程            intent.prepareToEnterProcess(                ActivityThread.isProtectedBroadcast(intent),                getAttributionSource()); // 检查签名权限保护[3](@ref)        }        return intent;    } catch (RemoteException e) {        throw e.rethrowFromSystemServer(); // 处理Binder通信异常    }}

通过 LoadedApk.getReceiverDispatcher() 将 BroadcastReceiver 包装为 IIntentReceiver(Binder接口),内部创建 ReceiverDispatcher 和 InnerReceiver(IIntentReceiver.Stub 实现类),构成双向通信桥梁

获取广播分发器 getReceiverDispatcher

桥接BroadcastReceiver和Binder通信,含InnerReceiver和Handler​​​​​

/** * 为BroadcastReceiver创建/获取跨进程通信的IIntentReceiver对象 * @param r 开发者定义的广播接收器实例 * @param context 关联的Context对象(通常为Activity/Service) * @param handler 指定回调线程的Handler(主线程Handler) * @param instrumentation 测试工具类(正常场景为null) * @param registered 是否为持久化注册(动态注册通常为true) * @return IIntentReceiver Binder接口对象,用于AMS跨进程回调 */public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,        Context context, Handler handler,        Instrumentation instrumentation, boolean registered) {    // 线程安全:通过synchronized保护mReceivers数据结构    synchronized (mReceivers) {        LoadedApk.ReceiverDispatcher rd = null;        ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;        // 1. 尝试从缓存中获取已注册的ReceiverDispatcher        if (registered) {            // 1.1 获取当前Context对应的接收器映射表            map = mReceivers.get(context);  // mReceivers: ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>>            if (map != null) {                rd = map.get(r);  // 检查是否已存在相同接收器的Dispatcher            }        }        // 2. 缓存未命中时创建新对象        if (rd == null) {            // 2.1 构造ReceiverDispatcher(含跨进程Binder对象)            rd = new ReceiverDispatcher(                mActivityThread.getApplicationThread(), // IApplicationThread对象                r,  // 用户定义的BroadcastReceiver                context,                handler,  // 主线程Handler(确保onReceive在主线程执行)                instrumentation,                registered            );            // 2.2 持久化注册时加入缓存            if (registered) {                if (map == null) {                    map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();                    mReceivers.put(context, map);  // 建立Context到接收器映射的关系                }                map.put(r, rd);  // 缓存键:BroadcastReceiver对象            }        } else {            // 3. 缓存命中时校验有效性            rd.validate(context, handler);  // 检查Context和Handler是否匹配        }        // 4. 标记为活跃状态(防止被意外回收)        rd.mForgotten = false;        // 5. 返回InnerReceiver(IIntentReceiver.Stub的实现类)        return rd.getIIntentReceiver();    }}

AMS注册广播接收器方法

动态注册核心步骤

ReceiverList管理:同一IIntentReceiver Binder对象对应一个ReceiverList,避免重复注册。

BroadcastFilter注册:将广播过滤器加入全局mReceiverResolver,后续广播分发时快速匹配

private Intent registerReceiverWithFeatureTraced(IApplicationThread caller, String callerPackage,         String callerFeatureId, String receiverId, IIntentReceiver receiver,         IntentFilter filter, String permission, int userId, int flags) {    // 1. 权限与调用者验证    enforceNotIsolatedCaller("registerReceiver"); // 禁止隔离进程调用    ProcessRecord callerApp = getRecordForAppLOSP(caller); // 获取调用者进程记录    // 验证调用者包名与进程匹配性(防止伪造身份)    if (!UserHandle.isCore(callerApp.info.uid) && !callerApp.getPkgList().containsKey(callerPackage)) {        throw new SecurityException("Package/Process mismatch");    }    // 2. 用户ID处理与优先级检查    userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, ...);    if (UserHandle.isCore(callingUid)) {        // 系统进程注册重要广播时需设置优先级(如USER_ACTION/PACKAGE_ACTION等)        if (filter.getPriority() == 0) {            filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); // 默认高优先级        }    }    // 3. 粘性广播(Sticky Broadcast)匹配    ArrayList<StickyBroadcast> stickyBroadcasts = null;    synchronized (mStickyBroadcasts) {        // 遍历IntentFilter的Action,查找匹配的粘性广播        for (String action : filter.actionsIterator()) {            for (int userId : {UserHandle.USER_ALL, callingUserId}) {                ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(userId);                if (stickies != null && stickies.containsKey(action)) {                    stickyBroadcasts.addAll(stickies.get(action)); // 收集匹配的粘性广播                }            }        }    }    // 4. 动态接收器注册核心逻辑    synchronized (this) {        // 4.1 创建或获取ReceiverList(同一Binder对象对应一个ReceiverList)        ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());        if (rl == null) {            rl = new ReceiverList(this, callerApp, callingPid, callingUid, userId, receiver);            if (rl.app != null) {                rl.app.mReceivers.addReceiver(rl); // 关联到进程记录            } else {                receiver.asBinder().linkToDeath(rl, 0); // 跨进程死亡监听            }            mRegisteredReceivers.put(receiver.asBinder(), rl); // 全局注册表记录        }        // 4.2 创建BroadcastFilter并注册到Resolver        BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, ...);        if (!rl.containsFilter(filter)) {            rl.add(bf); // 添加到ReceiverList            mReceiverResolver.addFilter(bf); // 注册到全局过滤器        }        // 5. 处理匹配的粘性广播(立即分发给新注册的接收器)        if (stickyBroadcasts != null) {            ArrayList<BroadcastFilter> receivers = new ArrayList<>();            receivers.add(bf);            for (StickyBroadcast broadcast : stickyBroadcasts) {                // 创建广播记录并加入队列                BroadcastRecord r = new BroadcastRecord(..., broadcast.intent, ..., receivers);                mBroadcastQueue.enqueueBroadcastLocked(r); // 异步分发            }        }        return stickyBroadcasts != null ? stickyBroadcasts.get(0).intent : null; // 返回首个粘性Intent    }}

广播注册流程中的LoaderApk和AMS的数据结构关系图

0

广播注册流程小结

1. App进程:打包“快递员”

需求发起:App调用registerReceiver,就像下单寄快递,告诉系统要接收哪种广播(IntentFilter)

包装处理:

ReceiverDispatcher:系统把开发者写的BroadcastReceiver打包成“快递员”,负责跨进程送货(IIntentReceiver接口)

InnerReceiver:这个“快递员”有个Binder工牌(IIntentReceiver.Stub),AMS凭工牌找到App的家(主线程)送货

2. 跨进程:向AMS“登记收货地址”

Binder快递:把“快递员”的工牌和收货要求(IntentFilter)通过Binder寄给AMS(registerReceiverWithFeature)

防重复注册:AMS用工牌(Binder对象)当身份证,检查是否已登记过,避免重复注册

3. AMS:建立“收货档案”

档案管理:

ReceiverList:以Binder工牌为Key,建一个“收货清单”,记录同一接收器的所有过滤条件

BroadcastFilter:把IntentFilter和接收器绑定,存到全局“快递分拣系统”(mReceiverResolver)

粘性广播:如果历史广播中有匹配的“包裹”,直接塞进分发队列(mBroadcastQueue)马上派送

关键角色比喻

组件

比喻

作用

ReceiverDispatcher

快递员+物流单

连接App和AMS,确保广播送到主线程

mRegisteredReceivers

客户档案库

用Binder工牌管理所有注册的接收器,避免重复

mReceiverResolver

智能分拣机

根据IntentFilter快速匹配接收器,类似快递分拣中心

mBroadcastQueue

快递派送车

分前台/后台/离线三种车队,决定广播的派送优先级

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/915619.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/915619.shtml
英文地址,请注明出处:http://en.pswp.cn/news/915619.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV计算机视觉实战(16)——图像分割技术

OpenCV计算机视觉实战&#xff08;16&#xff09;——图像分割技术0. 前言1. 分水岭算法1.1 应用场景1.2 实现过程2. GrabCut 交互式分割2.1 应用场景2.2 实现过程3. FloodFill3.1 应用场景3.2 实现过程小结系列链接0. 前言 图像分割是计算机视觉中将像素划分为具有特定语义或…

Coturn打洞服务器

* 概念理解&#xff1a;1. SDP协议&#xff1a;会话描述协议&#xff0c;视频通话的双方通过交换SDP信息进行媒体协商&#xff0c;从而选择使用某一相同的媒体协议进行通信&#xff1b;TLS协议&#xff1a;基于TCP的安全层传输协议DTLS协议&#xff1a;基于UDP的安全层传输协议…

python flusk 监控

# 创建虚拟环境目录 python3 -m venv /sda1/xunjian/venv # 激活虚拟环境 source /sda1/xunjian/venv/bin/activate # 激活后终端会显示 (venv)创建虚拟环境&#xff08;在当前目录&#xff09;&#xff1a;bashpython3 -m venv venv激活虚拟环境&#xff1a;bashsource venv/b…

VUE2 项目学习笔记 ? 语法 v-if/v-show

?语法页面渲染的时候&#xff0c;需要服务器传过来的对象中的一个属性&#xff0c;然后根据这个属性用v-for渲染标签&#xff0c;这里写的v-for".... in dataList.goodsList"但是当解析到这行语法的时候&#xff0c;dataList还没返回&#xff0c;因此控制台会报错找…

使用qemu命令启动虚拟机

1. 安装相关软件 yum install qemu edk2* libvirt -y 启动libvirt服务 systemctl start libvirtd systemctl status libvirtd2. 创建虚拟机 2.1. qemu启动命令示例 /usr/bin/qemu-system-loongarch64 \-machine virt,accelkvm \-nodefaults \-m 2048 \-smp 2,maxcpus4,co…

大模型系统化学习路线

人工智能大模型系统化学习路线一、基础理论筑基&#xff08;1-2个月) 目标&#xff1a;建立大模型核心认知框架 核心内容&#xff1a; 深度学习基础&#xff1a;神经网络原理、CNN/RNN结构、梯度下降算法大模型本质&#xff1a;Transformer架构&#xff08;重点掌握注意力机制、…

LLaMA-Factory 微调可配置的模型基本参数

LLaMA-Factory 微调可配置的模型基本参数 flyfish 基本参数 一、模型加载与路径配置参数名类型描述默认值model_name_or_pathOptional[str]模型路径&#xff08;本地路径或 Huggingface/ModelScope 路径&#xff09;。Noneadapter_name_or_pathOptional[str]适配器路径&#xf…

Ubuntu 22 安装 ZooKeeper 3.9.3 记录

Ubuntu 22 安装 ZooKeeper 3.9.3 记录 本文记录在 Ubuntu 22.04 系统上安装 ZooKeeper 3.9.3 的过程&#xff0c;包含 Java 环境准备、配置文件调整、启动与停机操作、以及如何将 ZooKeeper 注册为系统服务。 一、准备环境 ZooKeeper 3.9.x 要求 Java 11 或更高版本&#xff…

FreeSwitch通过Websocket(流式双向语音)对接AI实时语音大模型技术方案(mod_ppy_aduio_stream)

FreeSwitch通过WebSocket对接AI实时语音大模型插件技术方案1. 方案概述 基于FreeSWITCH的实时通信能力&#xff0c;通过WebSocket协议桥接AI大模型服务&#xff0c;实现低延迟、高并发的智能语音交互系统。支持双向语音流处理、实时ASR/TTS转换和动态业务指令执行。 1753095153…

航班调度优化策略全局概览

在机场关闭场景下的航班恢复工作&#xff0c;是将机场关闭期间所有的航班进行取消然后恢复还是将机场关闭期间航班全部延误而后调整呢&#xff1f;简单来说&#xff0c;在实际操作中&#xff0c;既不是无差别地全部取消&#xff0c;也不是无差别地全部延误。这两种“一刀切”的…

spring boot 异步线程@Async 传递 threadLocal数据

将父类的 threadLocal 的数据 在线程池时&#xff0c;可以转给子线程使用。 Async 的使用。 第一步在启动服务加上 EnableAsync 注解。 EnableAsync public class NetCoreApplication {... ... }第二步&#xff1a;导入阿里 线程工具类<dependency><groupId>com.a…

AI产品经理成长记《零号列车》第一集 邂逅0XAI列车

《零号列车》绝非传统意义上的 AI 产品经理教程 —— 它是我沉淀二十多年跨行业数字化转型与工业 4.0 实战经验后,首创的100集大型小说体培养指南。那些曾在千行百业验证过的知识与经验,不再是枯燥的文字堆砌,而是化作一场沉浸式的学习旅程。​ 这里没有生硬的理论灌输,而…

[C++11]范围for循环/using使用

范围for循环 范围for循环&#xff08;Range-based for loop&#xff09;是 C11 引入的一种简洁的循环语法&#xff0c;用于遍历容器中的元素或者其他支持迭代的数据结构。 范围for循环可以让代码更加简洁和易读&#xff0c;避免了传统for循环中索引的操作。 下面是范围for循环的…

简单了解下npm、yarn 和 pnpm 中 add 与 install(i) 命令的区别(附上两图带你一目明了)

目录 pnpm 中 add 和 i 的区别 npm 中 add 和 i 的区别 yarn 中 add 和 i 的区别 附上两图带你一目明了&#xff1a; npm、yarn和pnpm的三者区别图&#xff1a; i 和 add 的核心区别图&#xff1a; 个人建议&#xff1a;在项目中保持命令使用的一致性&#xff0c;选择一种…

ESP32-S3学习笔记<2>:GPIO的应用

ESP32-S3学习笔记&#xff1c;2&#xff1e;&#xff1a;GPIO的应用1. 头文件包含2. GPIO的配置2.1 pin_bit_mask2.2 mode2.3 pull_up_en和pull_down_en2.4 intr_type3. 设置GPIO输出/获取GPIO输入4. 中断的使用4.1 gpio_install_isr_service4.2 gpio_isr_handler_add4.3 gpio_…

得物视觉算法面试30问全景精解

得物视觉算法面试30问全景精解 ——潮流电商 商品鉴别 视觉智能&#xff1a;得物视觉算法面试核心考点全览 前言 得物App作为中国领先的潮流电商与鉴别平台&#xff0c;持续推动商品识别、真假鉴别、图像搜索、内容审核、智能推荐等视觉AI技术的创新与落地。得物视觉算法岗位…

[Linux入门] Linux 账号和权限管理入门:从基础到实践

一、Linux 用户账号&#xff1a;谁能访问系统&#xff1f; 1️⃣超级用户&#xff08;root&#xff09; 2️⃣普通用户 3️⃣程序用户 二、组账号&#xff1a;让用户管理更高效 1️⃣组的类型 2️⃣特殊组 三、用户与组的 “身份证”&#xff1a;UID 和 GID 四、配置文…

阿里云ssl证书自动安装及续订(acme)

目录 一、shell命令安装 二、docker run安装 三、docker compose安装 一、shell命令安装 # 安装acme curl https://get.acme.sh | sh -s emailfloxxx5163.com# 注册zerossl .acme.sh/acme.sh --register-account -m flowxxx25163.com --server zerossl# 获取证书 export Al…

@fullcalendar/vue 日历组件

功能&#xff1a;日程安排&#xff0c;展示日历&#xff0c;可以用来做会议日历&#xff0c;可以跨日期显示日程。 Fullcalendarvue3 日历组件 参考文档&#xff1a;【vue2】一个完整的日历组件 fullcalendar&#xff0c;会议预约功能 中文说明文档&#xff1a;https://www.he…

Dijkstra 算法求解多种操作

一、问题背景与核心需求 需要找到从a到b的最优操作序列&#xff0c;使得总花费最小。三种操作的规则为&#xff1a; 操作 1&#xff1a;x → x1&#xff0c;花费c1&#xff1b;操作 2&#xff1a;x → x-1&#xff0c;花费c2&#xff1b;操作 3&#xff1a;x → x*2&#xff0…