廣播是怎么注冊的呢?
階段 | 組件/數據結構 | 作用描述 | 存儲位置/關聯關系 |
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的數據結構關系圖
廣播注冊流程小結
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 | 快遞派送車 | 分前臺/后臺/離線三種車隊,決定廣播的派送優先級 |