主頁 > 軟體設計 > Android 四大組件之一:BroadCastReceiver動態注冊廣播流程

Android 四大組件之一:BroadCastReceiver動態注冊廣播流程

2021-10-06 08:58:36 軟體設計

動態注冊廣播流程

  • 動態注冊廣播流程
  • 廣播發送流程

動態注冊廣播流程

1.在Activity中動態注冊廣播時,呼叫registerReceiver方法,會呼叫到ContextWrapper的registerReceiver方法:
在這里插入圖片描述

2.這個方法內部,mBase其實就是一個ContextImpl型別的物件,所以會執行ContextImpl類的registerReceiver方法
在這里插入圖片描述

在這里插入圖片描述

經過registerReceiver多載最侄訓呼叫到registerReceiverInternal這個方法中,在這個方法中會先將scheduler先賦值為主執行緒的handler(上面傳的scheduler為null,這個Hanlder是后面用來分發ActivityManagerService發送過的廣播用的);
接下來創建一個IIntentReceiver型別的物件:如果mPackageinfo不為空呼叫mPackageInfo.getReceiverDispatcher創建,為空呼叫LoadedApk.ReceiverDispatcher創建(mPackageInfo是LoadedApk型別的物件),其實IIntentReceiver就是LoadedApk.ReceiverDispatcher.InnerReceiver型別的物件,由于InnerReceiver是繼承IIntentReceiver.Stub,所以這個InnerReceiver型別的物件其實就是一個Binder,它后面作為app行程和system_server行程進行通信的橋梁(AMS在收到相應的廣播時,就是通過這個Binder物件來通知MainActivity來接收的,),mPackageInfo是LoadedApk型別的物件,接著先分析LoadedApk類的getReceiverDispatcher方法的具體實作:

在這里插入圖片描述

在這里插入圖片描述

通過LoadedApk的getReceiverDispatcher方法,可以看出,它里面呼叫了LoadedApk.ReceiverDispatcher的構造方法,從上面貼出的代碼可以看到,這個構造方法里面創建了一個InnerReceiver的物件,并將這個物件賦值給mIIntentReceiver這個變數,后面通過getIIntentReceiver方法獲取的就是這個InnerReceiver型別的物件,
小結:
1.在新建廣播接收發布器ReceiverDispatcher時,會在建構式里面創建一個InnerReceiver實體,這是一個Binder物件,實作了IIntentReceiver介面,可以通過ReceiverDispatcher.getIIntentReceiver函式來獲得,獲得后就會把它傳給ActivityManagerService,以便接收廣播,
2.在ReceiverDispatcher類的建構式中,還會把傳進來的Handle型別的引數activityThread保存下來,以便后面在分發廣播的時候使用,

LockedApk中有一個mReceivers物件,以Context為key, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>為value,每一個Context都有一個這樣的hashmap,key就是receiver,value是ReceiverDispatcher,每一個receiver都對應一個ReceiverDispatcher,
總結:就是創建了一個RD物件(ReceiverDispatcher為了之后AMS和收到廣播之后遠程呼叫和可以通過handler插入到訊息佇列中去處理),這個是在LockedApk中創建的,首先LockedApk有一個mReceivers屬性,這個屬性里面存放的是key是Context,value是這個背景關系中所有注冊了receiver的集合,key是receiver,value是RD,這個RD構造方法里面保存了hander以便收到訊息之后添加到訊息佇列中,RD里面有一個靜態類InnerReceiver繼承自這個類IIntentReceiver.Stub是個代理所以可以通過他進行AMS到Rd中的通信
3.在完成了mPackageInfo.getReceiverDispatcher()創建InnerReceiver物件后,繼續呼叫了AMS的registerReceiver方法

在這里插入圖片描述

在這里插入圖片描述

拆開進行分析:
(1)首先獲取注冊的行程資訊
在這里插入圖片描述

(2)接著迭代filter的actions進行呼叫getStickLocked函式查找一下有沒有對應的sticky intent串列存在,
sticky intent串列:我們在最后一次呼叫sendStickyBroadcast函式來發送某個Action型別的廣播時,系統會把代表這個廣播的Intent保存下來,這樣,后來呼叫registerReceiver來注冊相同Action型別的廣播接收器,就會得到這個最后發出的廣播,這就是為什么叫做Sticky Intent了,這個最后發出的廣播雖然被處理完了,但是仍然被粘住在ActivityManagerService中,以便下一個注冊相應Action型別的廣播接收器還能繼承處理,
在這里插入圖片描述

(3)接下來把廣播接收器receiver保存一個ReceiverList串列中,這個串列的宿主行程是rl.app,在ActivityManagerService中,用一個行程記錄塊來表示這個應用程式行程,它里面有一個串列receivers,專門用來保存這個行程注冊的廣播接收器,接著,又把這個ReceiverList串列以receiver為Key值保存在ActivityManagerService的成員變數mRegisteredReceivers中,這些都是為了方便在收到廣播時,快速找到對應的廣播接收器的,
在這里插入圖片描述

(4)上面只是把廣播接收器receiver保存起來了,但是還沒有把它和filter關聯起來,這里就創建一個BroadcastFilter來把廣播接收器串列rl和filter關聯起來,然后保存在ActivityManagerService中的成員變數mReceiverResolver中去,

在這里插入圖片描述

這個方法內部,首先獲取了最后一個粘性訊息(如果有多個action,取最后一個action的最后一次粘性訊息,在最后進行回傳這個粘性訊息,如果注冊的receiver傳入的是null只傳入了actions,那么會直接回傳這個最后的粘性訊息),接著通過AMS的本地集合變數mRegisteredReceivers通過receiver(key)獲取rl(ReceiverList就是value)獲取不到進行創建rl,rl構造方法中傳入了行程記錄塊(callerApp)和Binder呼叫者的uid和pid,還有receiver,創建完后之后把當前rl添加到rl的行程記錄塊里面的receivers中,然后將receiver為key,rl為value保存添加到mRegisteredReceivers中;
receivers保存完之后開始保存filter,創建一個BroadcastFilter(將filter和rl傳入),接著把bf添加到rl中,并把bf添加到AMS本地集合mReceiverResolver中,
這樣注冊程序就完成了,

廣播發送流程

廣播的發送者將廣播發送到ActivityManagerService,ActivityManagerService接收到這個廣播以后,就會在自己的注冊中心查看有哪些廣播接收器訂閱了該廣播,然后把這個廣播逐一發送到這些廣播接收器中,但是ActivityManagerService并不等待廣播接收器處理這些廣播就回傳了,因此,廣播的發送和處理是異步的,概括來說,廣播的發送路徑就是從發送者到ActivityManagerService,再從ActivityManagerService到接收者,這中間的兩個程序都是通過Binder行程間通信機制來完成的

1.在Activity中發送廣播,sendBroadCast方法其實是呼叫了ContextWrapper的sendBroadcast方法,ContextWrapper類中的sendBroadcast方法,其實也是呼叫了ContextImpl的sendBroadcast方法
這里的resolvedType表示這個Intent的MIME型別

在這里插入圖片描述

  1. 這個方法中,通過行程間通信的的方式,呼叫了system_server行程的ActivityManagerService的broadcastIntent方法
    在這里插入圖片描述

在里面呼叫了broadcastIntentLocked方法,拆開進行分析:
3. 首先對Intent進行了處理,如果broadCastReceiver所在行程死亡那么將不會收到該廣播
在這里插入圖片描述

  1. 如果廣播沒有指定特性的接受者的話那么會呼叫到mReceiverResolver.queryIntent()這個方法中,在上面注冊的時候BroadcastFilter實體保存在了ActivityManagerService的成員變數mReceiverResolver中,這個BroadcastFilter實體包含了我們所注冊的廣播接收器,這里就通過mReceiverResolver.queryIntent函式將這個BroadcastFilter實體取回來,由于注冊一個廣播型別的接收器可能有多個,所以這里把所有符合條件的的BroadcastFilter實體放在一個List中,然后回傳來,
    在這里插入圖片描述

  2. 這里是查看一下這個intent的Intent.FLAG_RECEIVER_REPLACE_PENDING位有沒有設定,如果設定了的話,AMS就會在當前的系統中查看有沒有相同的intent還未被處理,如果有的話,就有當前這個新的intent來替換舊的intent,
    在這里插入圖片描述

  3. 這個if陳述句首先創建一個廣播記錄塊BroadcastRecord,里面記錄了這個廣播是由誰發出的以及要發給誰等相關資訊,由于前面得到的replacePending變數為false,這里得到的replaced變數的值也為false,于是執行下面的if陳述句,沒有替換時,才需要將新的廣播加入到BroadcastQueue.mParallelBroadcasts佇列中,等待進一步處理;進一步處理的操作由函式scheduleBroadcastsLocked進行,處理完成之后會把NR重置

在這里插入圖片描述

上面分析的是動態廣播(NR>0因為是在動態regist的時候會把bf添加到mReceiverResolver這個AMS本地變數中,如果是靜態的是查詢不到mReceiverResolver變數里面的bf),接下來分析靜態注冊的廣播,靜態注冊的廣播receivers不為null,

小結:
1.先獲取動態注冊的廣播和靜態注冊的廣播,并將這些廣播分別存盤到兩個不同的串列中

2.判斷是否是發送的普通廣播,如果是,并且動態注冊的廣播,則先將動態注冊的廣播發送, 走后續的廣播發送流程,接著判斷是否有靜態廣播,如果有靜態廣播,則發送靜態廣播,(從原始碼代碼邏輯可以知道,普通廣播的發送,是動態廣播優先靜態廣播發送)如果發送的是有序廣播,則會將第一步的兩個串列合并到receivers串列中,并按照優先級對廣播進行排序,具體的排序規則是,優先級高的排前面,對于相同優先級的,動態優先靜態,對于優先級相同,廣播型別相同,如果都是動態廣播型別,則先注冊的優先后注冊的,對于都是靜態廣播型別,則先掃描的由于后掃描的,

3.將廣播將第二步的廣播串列,添加到BroadcastQueue的mParallelBroadcasts或者mOrderedBroadcasts中,如果廣播按照普通方式發送,則將廣播存盤在mParallelBroadcasts串列中,如果是靜態廣播存盤在mOrderedBroadcasts串列中,如果發送方式是有序廣播,則將所有的廣播(不管是靜態注冊的還是動態注冊的)都存盤在mOrderedBroadcasts串列中,

在這里插入圖片描述

  1. 動態廣播:通過呼叫BroadcastQueue的enqueueOrderedBroadcastLocked方法將發送的廣播存盤在BroadcastQueue的mParallelBroadcasts串列中,然后通過scheduleBroadcastsLocked方法發送廣播,

這里的mBroadcastsScheduled表示ActivityManagerService當前是不是正在處理其它廣播,如果是的話,這里就先不處理直接回傳了,保證所有廣播串行處理,注意這里處理廣播的方式,它是通過訊息回圈來處理,每當AMS接收到一個廣播時,它就把這個廣播放進自己的訊息佇列去就完事了,根本不管這個廣播后續是處理的,因此,這里我們可以看出廣播的發送和處理是異步的,這里的成員變數mHandler是一個在ActivityManagerService內部定義的Handler類變數,通過它的sendEmptyMessage函式把一個型別為BROADCAST_INTENT_MSG的空訊息放進AMS的訊息佇列中去,這里的空訊息是指這個訊息除了有型別資訊之外,沒有任何其它額外的資訊,因為前面已經把要處理的廣播資訊都保存在mParcelBroadcasts中了,等處理這個訊息時,從mParcelBroadcasts就可以讀回相關的廣播資訊了,因此,這里不需要把廣播資訊再放在訊息內容中,

在這里插入圖片描述

  1. 接下來分析processNextBroadcast函式,這個函式有點長進行分步分析
    在這里插入圖片描述

(8.1)這里傳進來的引數fromMsg為true,于是把mBroadcastScheduled重新設為false,這樣,下一個廣播就能進入到訊息佇列中進行處理了,
在這里插入圖片描述

(8.2)無序廣播,并呼叫deliverToRegisteredReceiverLocked方法發送無序廣播
在這里插入圖片描述

(8.3)如果是發送的有序廣播,則判斷廣播所屬的行程是否存在,并且行程未被kill,則執行processCurBroadcastLocked處理后續的流程,如果廣播所屬的行程不存在,則新建一個行程,并重復廣播的發送程序,這些情況的后續流程,如果只是發送普通股廣播,并且廣播就所屬當前行程,那么直接執行deliverToRegisteredReceiverLocked方法
在這里插入圖片描述

(8.4)deliverToRegisteredReceiverLocked方法中,又會呼叫到performReceiveLocked方法,performReceiveLocked方法中,會呼叫app.thread.scheduleRegisteredReceiver方法,app.thread其實是ApplicationThread型別的物件,這個在之前就分析過,代碼的執行又回到了ApplicationThread類中
前面bt中app屬性保存的就是注冊廣播的行程,receiver屬性保存的是對應的所有廣播接收器,
在這里插入圖片描述

在這里插入圖片描述

(8.5)這個receiver就是上面ASM注冊廣播的時候傳進來的LockedApk中的InnerReceiver
在這里插入圖片描述

(8.6)在注冊的時候rd會在構造方法中創建InnerReceiver,并把當前rd傳入到InnerReceiver的建構式中,并用弱參考保存rd
在這里插入圖片描述

(8.7)把訊息放在訊息佇列中,然后就回傳了,這個訊息最侄訓在傳進來的Runnable型別的引數的run成員函式中進行處理,
在這里插入圖片描述

(8.8)分發到了接收器的onReceiver方法中
在這里插入圖片描述

參考鏈接:
https://blog.csdn.net/luoshengyang/article/details/6730748

https://blog.csdn.net/luoshengyang/article/details/6737352

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/305744.html

標籤:其他

上一篇:簡單易上手的通訊錄實作,跟著敲,你也能擁有屬于你的通訊錄!(代碼+筆記已經上傳至gitee)

下一篇:學透CSS- :root + vm/vh 實作回應式字體!!!

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more