?(十一)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)在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。