?(十一)BroadcastReceiver
?BroadcastReceiver评姨,也就是廣播,簡稱Receiver萤晴。
?很多App開發(fā)人員表示吐句,從來沒用過Receiver。其實(shí)吧店读,對(duì)于音樂播放類App嗦枢,用Service和Receiver還是蠻多的,如果你用過QQ音樂屯断,App退到后臺(tái)文虏,音樂照樣播放不會(huì)停止,這就是你寫的Service在后臺(tái)起作用裹纳。
?在前臺(tái)的Activity择葡,點(diǎn)擊停止按鈕,就會(huì)給后臺(tái)Service發(fā)送一個(gè)Receiver剃氧,通知它停止播放音樂敏储;點(diǎn)擊播放按鈕,仍然是發(fā)送這個(gè)Receiver朋鞍,只是攜帶的值變了已添,所以Service收到請(qǐng)求后播放音樂妥箕。
?反過來,后臺(tái)Service播放每播放完一首音樂更舞,接下來準(zhǔn)備播放下一首音樂的時(shí)候畦幢,就會(huì)給前臺(tái)Activity發(fā)Receiver,讓Activity顯示下一首音樂的名稱缆蝉。
?所以音樂播放器的原理宇葱,就是一個(gè)前后臺(tái)Activity和Service互相發(fā)送和接收Receiver的過程。
?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檢查咸产。接下來你想到什么矢否?對(duì),Receiver的插件化解決方案锐朴,就是這個(gè)思路兴喂。
? 接下來我們看Receiver是怎么和AMS打交道的,分為兩部分焚志,一是注冊(cè)衣迷,二是發(fā)送廣播。
?你只有注冊(cè)了這個(gè)廣播酱酬,發(fā)送這個(gè)廣播時(shí)壶谒,才能通知到你,執(zhí)行onReceive方法膳沽。
?我們就拿音樂播放器來舉例汗菜,在Activity注冊(cè)Receiver,在Service發(fā)送廣播挑社。Service播放下一首音樂時(shí)陨界,會(huì)通知Activity修改當(dāng)前正在播放的音樂名稱。
?注冊(cè)過程如下所示:
- 1)在Activity中痛阻,注冊(cè)Receiver菌瘪,并通知AMS。
?這里Activity使用了Context提供的registerReceiver方法,然后通過AMN/AMP俏扩,把一個(gè)receiver傳給AMS糜工。
?在創(chuàng)建這個(gè)Receiver對(duì)象的時(shí)候,需要為receiver指定IntentFilter录淡,這個(gè)filter就是Receiver的身份證捌木,用來描述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ā)給誰岸㈣搿?所以Activity所在的進(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存起來。
?動(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中挂洛,通過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)程收到廣播鹅龄,并沒有把廣播直接發(fā)給receiver,而是將廣播封裝成一個(gè)消息亭畜,發(fā)送到主線程的消息隊(duì)列中扮休,當(dāng)這個(gè)消息被處理時(shí),才會(huì)把這個(gè)消息中的廣播發(fā)送給receiver拴鸵。
?我們下面通過圖玷坠,仔細(xì)看一下這3個(gè)階段:
?第1步,Service發(fā)送廣播給AMS
?發(fā)送廣播劲藐,是通過Intent這個(gè)參數(shù)八堡,攜帶了Filter,從而告訴AMS瘩燥,什么樣的receiver能接受這個(gè)廣播秕重。
?第2步,AMS接收廣播厉膀,發(fā)送廣播溶耘。
?接收廣播和發(fā)送廣播是不同步的。AMS每接收到一個(gè)廣播服鹅,就把它扔到廣播發(fā)送隊(duì)列中凳兵,至于發(fā)送是否成功,它就不管了企软。
?因?yàn)閞eceiver分為無序receiver和有序receiver庐扫,所以廣播發(fā)送隊(duì)列也分為兩個(gè),分別發(fā)送這兩種廣播。
?AMS發(fā)送廣播給客戶端形庭,這又是一個(gè)跨進(jìn)程通信铅辞,還是通過ATP,把消息發(fā)給APT萨醒。因?yàn)橐獋鬟fReceiver這個(gè)對(duì)象斟珊,所以它也是一個(gè)Binder對(duì)象,才可以傳過去富纸。我們前面說過囤踩,在把Receiver注冊(cè)到AMS的時(shí)候,會(huì)把Receiver封裝為一個(gè)IIntentReceiver接口的Binder對(duì)象晓褪。那么接下來堵漱,AMS就是把這個(gè)IIntentReceiver接口對(duì)象傳回來。
?第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來發(fā)送
?3)接下來要走的路就是我們所熟悉的了阔拳,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ì)通過AMN/AMP發(fā)送一個(gè)消息個(gè)AMS,告訴AMS搔预,廣播發(fā)送成功了霹期。AMS得到通知后,就發(fā)送廣播給下一個(gè)Receiver拯田。
?注意:InnerReceiver是IIntentReceiver的stub历造,是Binder對(duì)象的接收端。
?廣播的種類
?Android廣播按發(fā)送方式分類有三種:無序廣播、有序廣播(OrderedBroadcast)和粘性廣播(StickyBroadcast)吭产。
?1)無序廣播是最普通的廣播侣监。
?2)有序廣播區(qū)別于無序廣播,就在于它可以指定優(yōu)先級(jí)臣淤。
這兩種receiver存在AMS不同的變量中橄霉,可以認(rèn)為是兩個(gè)receiver集合,發(fā)送不同類別的廣播荒典。
?3)粘性廣播是無序廣播的一種酪劫。
?粘性廣播,我們平常見的不多寺董,但我說一個(gè)場景你就明白了覆糟,那就是電池電量。當(dāng)電量小于20%的時(shí)候遮咖,就會(huì)提示用戶滩字。
?而獲取電池的電量信息,就是通過廣播來實(shí)現(xiàn)的御吞。
?但是一般的廣播麦箍,發(fā)完就完了。我們需要有這樣一種廣播陶珠,發(fā)出后挟裂,還能一直存在,未來的注冊(cè)者也能收到這個(gè)廣播揍诽,這種廣播就是粘性廣播诀蓉。
?由于動(dòng)態(tài)receiver只能在Activity的onCreate()方法調(diào)用時(shí)才能注冊(cè)再接收廣播,所以當(dāng)程序沒有運(yùn)行就不能接受到廣播暑脆;但是靜態(tài)注冊(cè)的則不依賴于程序是否處于運(yùn)行狀態(tài)渠啤。
?至此,關(guān)于廣播的所有概念添吗,就全都介紹完了沥曹,就連我都比較驚訝的是,我居然沒貼什么代碼碟联。希望上述這兩千多字妓美,能引導(dǎo)App開發(fā)人員進(jìn)入一個(gè)神奇的世界。
?下一篇文章鲤孵,我們?nèi)タ纯碈ontentProvider部脚。