Android 發送接收廣播原理

前言

本文代碼基于 Android R。

注冊廣播接收者

Android 可以動態和靜態注冊廣播接收者。動態廣播接收者注冊后被 AMS 存儲在其 IntentResolver 數據結構中。而靜態廣播接收者開機后被 PMS 讀取解析并存儲在其 IntentResolver 中。

動態注冊廣播接收者

應用組件向 AMS 注冊 IIntentReceiver 和 IntentFilter ,當 AMS 派發其它組件請求派發的 Intent 時,通過其注冊的 IntentFilter 來判斷是否與派發的 Intent 匹配,如果匹配,AMS 調用 IIntentReceiver 通知組件處理收到 Intent 后的操作。

每個 BroadcastReceiver 都會有其對應的 IIntentReceiver ,它由其 Context 構建。IIntentReceiver 被發送給 AMS 后與其進程信息 ProcessRecord 被封裝在 ReceiverList 中,在發現匹配到的 Intent 時用來尋找 IIntentReceiver 對應進程。而 ReceiverList 被放在由 IntentFilter 構建的 BroadcastFilter 中,最后 BroadcastFilter 被緩存在 AMS 的 IntentResolver 中。
動態注冊廣播接收者

動態注冊接收者

AMS 在分發一個 Intent 時,用 IntentResolver 中緩存的 BroadcastFilter 的 IntentFilter 和 Intent 相匹配,取出匹配成功的 BroadcastFilter 列表。再取出每個 BroadcastFilter 中的 ReceiverList ,獲取其 IIntentReceiver 和進程信息,把 Intent 通過進程的 IApplicationThread 發送給對應的 IIntentReceiver ,再發送給 IIntentReceiver 對應的 BroadcastReceiver 處理。
查找動態注冊的廣播接收者

代碼流程為:
動態注冊廣播接收者代碼流程

靜態注冊廣播接收者

靜態注冊廣播接收者,那在 AndroidManifest.xml 中聲明 <receiver> ,并使用匹配器 IntentFilter 配置它要匹配的數據。

開機后,PMS 會去讀取每個 pkg 的信息,并把每個 pkg 里的每個組件信息存儲到 ComponentResolver 中,receiver 組件的信息存儲在 ComponentResolver#mReceivers 中。mReceivers 是一個 IntentResolver<F, ResolveInfo>,其方法 newResult() 負責將 F 轉換成 ResolveInfo。其中 F 為從 pkg 中讀取的 receiver 和 IntentFilter 的 Pair。
注冊靜態接收者

靜態注冊廣播接收者

AMS 在分發一個 Intent 時,會根據 Intent 封裝的數據格式去 PMS ComponentResolver 的 mReceivers 中取出對應的 Map 來查找,取出對應 Map 中的 [ParsedActivity, IntentFilter] 對,判斷 IntentFilter 是否和 Intent 匹配,如果匹配,通過 IntentFilter#newResult 方法將 [ParsedActivity, IntentFilter] 轉換成 ResolveInfo 返回給 AMS。AMS 根據 ResolveInfo 中的 ActivityInfo 找到接收者進程,把 Intent 發送給接收者進程后,再實例化一個和 ActivityInfo 對應的 BroadcastReceiver 實例。


獲取靜態廣播接收者

PMS 查找和 Intent 對應的 ResolveInfo

分發廣播

根據 AMS 分發廣播的方式,可以分為串行分發和并行分發。

當 Intent 設置為有序分發(ordered)時,它會被串行分發。
當接收者為靜態注冊時,發送給它們的 Intent 會被串行分發。
僅 Intent 沒有設置為有序分發,且接收者為動態注冊,才會被并行分發。

并行分發不關注分發結果,一次性全部分發給所有接收者,并在串行分發前分發。
串行分發需要等上一個接收者處理完成后才會分發給下一個接收者。


發送廣播

并行分發廣播

AMS 為 Intent 和所有并行接收者創建廣播 BroadcastRecord,再根據 Intent 設置的屬性判斷它是由前臺還是后臺或是耗時 BroadcastQueue 分發。BroadcastRecord 會被插到對應 BroadcastQueue 并行廣播列表的末尾,等待派發。


AMS 并行分發廣播

BroadcastQueue 分發廣播

BroadcastQueue 分發并行廣播

串行分發廣播

如果 Intent 被要求有序派發,AMS 將它的所有接收者和 Intent 封裝成 BroadcastRecord ,找到要派發它的 BroadcastQueue ,然后把 BroadcastRecord 插入到對應 BroadcastQueue 的 BroadcastDispatcher 的有序廣播隊列中等待派發。如果是非有序派發的 Intent,AMS 將其所有靜態注冊的接收者和 Intent 封裝成 BroadcastRecord。

BroadcastDispatcher 負責為 BroadcastQueue 串行派發廣播。它加入了一個延遲策略,來特別關注耗時的接收者,以減少這些耗時者對性能效率的影響。延遲策略下一小節將單獨描述。


AMS 串行分發廣播

BroadcastQueue 分發串行廣播

延遲策略

延遲策略主要是為了減小耗時接收者對整個廣播發送性能產生的影響。當一個接收者接收并處理 Intent 耗時超過系統設置的域值(默認 5s)時,這個接收者所在的進程被認為是低效進程,所有之后發送給它的 Intent 將被延遲發送,直到系統判定它恢復了正常。

當一個進程被判定為低效進程時,BroadcastDispatcher 為它創建一個 Deferrals ,負責管理延遲發送給他的 Intent:它包含了延遲開始時間、延遲時長、延遲結束時間、延遲的廣播列表(BroadcastQueue 會為某個 Intent 在這個進程中的所有接收者和 Intent 單獨創建一個廣播 BroadcastQueue,因此這個廣播列表中包含了一隊由不同的 Intent 和其在該進程中的接收者構成的廣播)。

當一個進程的 Deferrals 被創建后會被插入到 BroadcastDispatcher 的延遲進程廣播列表中,當其延遲到期后,會優先在下一次派發時派發。派發時會更新下一次的延遲時長(即 Deferrals 的廣播列表中下一個廣播被延遲派發的時長),默認為 上一次延遲時長*縮短因子(0.75f)。

當被延遲的進程正在接收 alarm 廣播時,它的 Deferrals 會被移到 Alarm 延遲進程廣播列表,直到它處理完 alarm 廣播處理。Alarm 延遲進程廣播列表中的廣播具有最高優先級的派發順序。

當 Deferrals 進程中的某個接收者在處理一個 Intent 時仍耗時超過 5s ,它下一次的延遲時長會被重新設置為 5s。

當系統中所有的串行廣播都被發送完成時,會立即派發延遲進程廣播隊列中的廣播而不管它是否到期。

接收者在接收處理某個 Intent 結束時,AMS 計算其耗時并判斷這個接收者所在進程是否為低效進程,是否需要對它使用延遲策略:
廣播發送給某個 receiver 完成

延遲策略概要:
延遲規則設計

總結

Intent 會根據其發送者要求的發送規則(是否有序)和接收者的注冊方式被 AMS 以并行或串行方式分發。

Intent 和其接收者列表構成廣播,插入到 AMS 為它分配的 BroadcastQueue 的并行或串行廣播隊列中,等待派發。

并行派發不關注接收者的處理結果。串行派發關注處理結果,如果處理耗時會對接收者進程采用延遲策略,且僅當上一個接收者接收完后才會發送給下一個接收者。ordered 廣播發送給動態注冊的接收者時,通過 BroadcastRecord.state 來判斷上一個接收者是否接收完成,發送給 receiver 之前為 CALL_IN_SERVICE,發送之后為 CALL_DONE_RECEIVE,接收者處理完后為 IDLE。


發送廣播

原創文章,歡迎轉載,但請注明出處。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容