寫給Android App開發(fā)人員看的Android底層知識(shí)(6)

?(十一)BroadcastReceiver

?BroadcastReceiver,也就是廣播,簡(jiǎn)稱Receiver。

?很多App開發(fā)人員表示,從來(lái)沒(méi)用過(guò)Receiver。其實(shí)吧,對(duì)于音樂(lè)播放類App,用Service和Receiver還是蠻多的,如果你用過(guò)QQ音樂(lè),App退到后臺(tái),音樂(lè)照樣播放不會(huì)停止,這就是你寫的Service在后臺(tái)起作用。

?在前臺(tái)的Activity,點(diǎn)擊停止按鈕,就會(huì)給后臺(tái)Service發(fā)送一個(gè)Receiver,通知它停止播放音樂(lè);點(diǎn)擊播放按鈕,仍然是發(fā)送這個(gè)Receiver,只是攜帶的值變了,所以Service收到請(qǐng)求后播放音樂(lè)。

?反過(guò)來(lái),后臺(tái)Service播放每播放完一首音樂(lè),接下來(lái)準(zhǔn)備播放下一首音樂(lè)的時(shí)候,就會(huì)給前臺(tái)Activity發(fā)Receiver,讓Activity顯示下一首音樂(lè)的名稱。

?所以音樂(lè)播放器的原理,就是一個(gè)前后臺(tái)Activity和Service互相發(fā)送和接收Receiver的過(guò)程。

?Receiver分靜態(tài)廣播和動(dòng)態(tài)廣播兩種。

?在Manifest中聲明的Receiver,是靜態(tài)廣播:

在程序中手動(dòng)寫注冊(cè)代碼的,是動(dòng)態(tài)廣播:

?二者具有相同的功能,只是寫法不同。既然如此,我們就可以把所有靜態(tài)廣播,都改為動(dòng)態(tài)廣播,這就省的在Manifest文件中聲明了,避免了AMS檢查。接下來(lái)你想到什么?對(duì),Receiver的插件化解決方案,就是這個(gè)思路。

? 接下來(lái)我們看Receiver是怎么和AMS打交道的,分為兩部分,一是注冊(cè),二是發(fā)送廣播。

?你只有注冊(cè)了這個(gè)廣播,發(fā)送這個(gè)廣播時(shí),才能通知到你,執(zhí)行onReceive方法。

?我們就拿音樂(lè)播放器來(lái)舉例,在Activity注冊(cè)Receiver,在Service發(fā)送廣播。Service播放下一首音樂(lè)時(shí),會(huì)通知Activity修改當(dāng)前正在播放的音樂(lè)名稱。

?注冊(cè)過(guò)程如下所示:

  1. 1)在Activity中,注冊(cè)Receiver,并通知AMS。

?這里Activity使用了Context提供的registerReceiver方法,然后通過(guò)AMN/AMP,把一個(gè)receiver傳給AMS。

?在創(chuàng)建這個(gè)Receiver對(duì)象的時(shí)候,需要為receiver指定IntentFilter,這個(gè)filter就是Receiver的身份證,用來(lái)描述receiver。

?在Context的registerReceiver方法中,它會(huì)使用PMS獲取到包的信息,也就是LoadedApk對(duì)象。

?就是這個(gè)LoadedApk對(duì)象,它的getReceiverDispatcher方法,將Receiver封裝成一個(gè)實(shí)現(xiàn)了IIntentReceiver接口的Binder對(duì)象。

?我們就是將這個(gè)Binder對(duì)象和filter傳遞給AMS。

?只傳遞Receiver給AMS是不夠的,當(dāng)發(fā)送廣播時(shí),AMS不知道該發(fā)給誰(shuí)?。克訟ctivity所在的進(jìn)程還要把自身對(duì)象也發(fā)送給AMS。

? 2)AMS收到消息后,就會(huì)把上面這些信息,存在一個(gè)列表中,這個(gè)列表中保存了所有的Receiver。

?注意了,這里忙活半天,都是在注冊(cè)動(dòng)態(tài)receiver。

?靜態(tài)receiver什么時(shí)候注冊(cè)到AMS的呢?是在App安裝的時(shí)候。PMS會(huì)解析Manifest中的四大組件信息,把其中的receiver存起來(lái)。

?動(dòng)態(tài)receiver和靜態(tài)receiver分別存在AMS不同的變量中,在發(fā)送廣播的時(shí)候,會(huì)把兩種receiver合并到一起,然后以此發(fā)送。其中動(dòng)態(tài)的排在靜態(tài)的前面,所以動(dòng)態(tài)receiver永遠(yuǎn)優(yōu)先于靜態(tài)receiver收到消息。

?此外,Android系統(tǒng)每次啟動(dòng)的時(shí)候,也會(huì)把靜態(tài)廣播接收者注冊(cè)到AMS。因?yàn)锳ndroid系統(tǒng)每次啟動(dòng)時(shí),都會(huì)重新安裝所有的apk,詳細(xì)流程,我們會(huì)在后面PMS的相關(guān)章節(jié)看到。

?- - - - - - - - - - - - - - - 華麗的分界線------------------------

?發(fā)送廣播的流程如下:

?1)在Service中,通過(guò)AMM/AMP,發(fā)送廣播給AMS,廣播中攜帶著Filter。

?2)AMS收到這個(gè)廣播后,在receiver列表中,根據(jù)filter找到對(duì)應(yīng)的receiver,可能是多個(gè),把它們都放到一個(gè)廣播隊(duì)列中。最后向AMS的消息隊(duì)列發(fā)送一個(gè)消息。

?當(dāng)消息隊(duì)列中的這個(gè)消息被處理時(shí),AMS就從廣播隊(duì)列中找到合適的receiver,向廣播接收者所在的進(jìn)程發(fā)送廣播。

?3)receiver所在的進(jìn)程收到廣播,并沒(méi)有把廣播直接發(fā)給receiver,而是將廣播封裝成一個(gè)消息,發(fā)送到主線程的消息隊(duì)列中,當(dāng)這個(gè)消息被處理時(shí),才會(huì)把這個(gè)消息中的廣播發(fā)送給receiver。

?我們下面通過(guò)圖,仔細(xì)看一下這3個(gè)階段:

?第1步,Service發(fā)送廣播給AMS

?發(fā)送廣播,是通過(guò)Intent這個(gè)參數(shù),攜帶了Filter,從而告訴AMS,什么樣的receiver能接受這個(gè)廣播。

?第2步,AMS接收廣播,發(fā)送廣播。

?接收廣播和發(fā)送廣播是不同步的。AMS每接收到一個(gè)廣播,就把它扔到廣播發(fā)送隊(duì)列中,至于發(fā)送是否成功,它就不管了。

?因?yàn)閞eceiver分為無(wú)序receiver和有序receiver,所以廣播發(fā)送隊(duì)列也分為兩個(gè),分別發(fā)送這兩種廣播。

?AMS發(fā)送廣播給客戶端,這又是一個(gè)跨進(jìn)程通信,還是通過(guò)ATP,把消息發(fā)給APT。因?yàn)橐獋鬟fReceiver這個(gè)對(duì)象,所以它也是一個(gè)Binder對(duì)象,才可以傳過(guò)去。我們前面說(shuō)過(guò),在把Receiver注冊(cè)到AMS的時(shí)候,會(huì)把Receiver封裝為一個(gè)IIntentReceiver接口的Binder對(duì)象。那么接下來(lái),AMS就是把這個(gè)IIntentReceiver接口對(duì)象傳回來(lái)。

?第3步,App處理廣播

?這個(gè)流程描述如下:

?1)消息從AMS傳到客戶端,把AMS中的IIntentReceiver接口對(duì)象轉(zhuǎn)為InnerReceiver對(duì)象,這就是receiver,這是一個(gè)AIDL跨進(jìn)程通信。

v2)然后在ReceiverDispatcher中封裝一個(gè)Args對(duì)象(這是一個(gè)Runnable對(duì)象,要實(shí)現(xiàn)run方法),包括廣播接收者所需要的所有信息,交給ActivtyThread來(lái)發(fā)送

?3)接下來(lái)要走的路就是我們所熟悉的了,ActivtyThread把Args消息扔到H這個(gè)Hanlder中,向主線程消息隊(duì)列發(fā)送消息。等到執(zhí)行Args消息的時(shí)候,自然是執(zhí)行Args的run方法。

?4)在Args的run方法中,實(shí)例化一個(gè)Receiver對(duì)象,調(diào)用它的onReceiver方法。

?5)最后,在Args的run方法中,隨著Receiver的onReceiver方法調(diào)用結(jié)束,會(huì)通過(guò)AMN/AMP發(fā)送一個(gè)消息個(gè)AMS,告訴AMS,廣播發(fā)送成功了。AMS得到通知后,就發(fā)送廣播給下一個(gè)Receiver。

?注意:InnerReceiver是IIntentReceiver的stub,是Binder對(duì)象的接收端。

?廣播的種類

?Android廣播按發(fā)送方式分類有三種:無(wú)序廣播、有序廣播(OrderedBroadcast)和粘性廣播(StickyBroadcast)。

?1)無(wú)序廣播是最普通的廣播。

?2)有序廣播區(qū)別于無(wú)序廣播,就在于它可以指定優(yōu)先級(jí)。

這兩種receiver存在AMS不同的變量中,可以認(rèn)為是兩個(gè)receiver集合,發(fā)送不同類別的廣播。

?3)粘性廣播是無(wú)序廣播的一種。

?粘性廣播,我們平常見(jiàn)的不多,但我說(shuō)一個(gè)場(chǎng)景你就明白了,那就是電池電量。當(dāng)電量小于20%的時(shí)候,就會(huì)提示用戶。

?而獲取電池的電量信息,就是通過(guò)廣播來(lái)實(shí)現(xiàn)的。

?但是一般的廣播,發(fā)完就完了。我們需要有這樣一種廣播,發(fā)出后,還能一直存在,未來(lái)的注冊(cè)者也能收到這個(gè)廣播,這種廣播就是粘性廣播。

?由于動(dòng)態(tài)receiver只能在Activity的onCreate()方法調(diào)用時(shí)才能注冊(cè)再接收廣播,所以當(dāng)程序沒(méi)有運(yùn)行就不能接受到廣播;但是靜態(tài)注冊(cè)的則不依賴于程序是否處于運(yùn)行狀態(tài)。

?至此,關(guān)于廣播的所有概念,就全都介紹完了,就連我都比較驚訝的是,我居然沒(méi)貼什么代碼。希望上述這兩千多字,能引導(dǎo)App開發(fā)人員進(jìn)入一個(gè)神奇的世界。

?下一篇文章,我們?nèi)タ纯碈ontentProvider。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容