Android 廣播內(nèi)容全知道

版權(quán)聲明:

本賬號發(fā)布文章均來自公眾號,承香墨影(cxmyDev),版權(quán)歸承香墨影所有。

未經(jīng)允許,不得轉(zhuǎn)載。

一、前言

Broadcast 是 Android 四大組件之一,與傳統(tǒng)意義上的電臺廣播類似,一個廣播需要有一個發(fā)布者,以及任意多個接收者,并且它的特點也非常的明顯,發(fā)布者只負責將廣播發(fā)布出去,而不去關(guān)心接收者是否能正常接收到廣播內(nèi)容,也不關(guān)心接收者是如何處理廣播的。以這種形式來達到發(fā)送者和接收者完全的解耦。

Broadcast 可以被大致分為三個角色:發(fā)送者、接收者(BroadcastReceiver)以及承載 Broadcast 的 Intent 對象。接下來就這三個角色進行單獨講解。

二、Broadcast 的類型

在 Android 中,為了適應(yīng)不同的場景,Broadcast 可以被分為:

  • 無序廣播。
  • 有序廣播。
  • 本地廣播。
  • Sticky 廣播。

先來看看對于不同類型廣播的特點。

1、無序廣播

無序廣播是完全異步的,通過 Context.sendBroadcast() 方法來發(fā)送,從效率上來看,還算是比較高的。

sendBroadcast() 方法中,還有一個第二個參數(shù)為 String 類型的重載方法,它是用來設(shè)定接收者的權(quán)限的,這個權(quán)限可以是系統(tǒng)權(quán)限,也可以是自定義的權(quán)限。

但是正如它的名稱一樣,無序廣播對所有廣播接收者(Receivers)而言,是無序的,也就是說,所有接收者無法確定接收時序的順序,這樣也導致了,無序廣播無法被停止。當它被發(fā)送出去之后,它將通知所有這條廣播的接收者,直到?jīng)]有與之匹配的廣播接收者為止。

2、有序廣播

有序廣播通過 Context.sendOrderedBroadcast() 方法來發(fā)送。有序廣播和無序廣播最大的不同,就是它可以允許接收者設(shè)定優(yōu)先級,它會按照接收者設(shè)定的優(yōu)先級依次傳播。而高優(yōu)先級的接收者,可以對廣播的數(shù)據(jù)進行處理或者停止掉此條廣播的繼續(xù)傳播。

想要設(shè)定有序廣播的優(yōu)先級,需要在 IntentFilter 中進行設(shè)定。在 AndroidManifest.xml 中,使用 android:priority 屬性設(shè)置,在代碼中,可以通過 IntentFilter.setPriority() 方法設(shè)定。這取決于 Broadcast 的注冊方式。

可以看到,它的取值是有限定范圍的,需要在 SYSTEM_LOW_PRIORITY 和 SYSTEM_HIGH_PRIORITY 之間。

可以看到,這樣的 priority 的限定范圍,就是在 -1000 ~ 1000 之間,而如果不對其進行設(shè)定,它的默認值為 0。

前面也提到,高優(yōu)先級的接收者可以附加數(shù)據(jù)以及停止當前廣播的傳播。附加數(shù)據(jù),可以通過 setResult() 方法來操作,同時也可以通過 getResult() 方法來獲取比自己更高優(yōu)先級的接收者設(shè)置的數(shù)據(jù)內(nèi)容。而停止這條廣播繼續(xù)傳播,可以調(diào)用 abortBroadcast() 方法。

3、Sticky廣播

Sticky 廣播和它的名字很像,它是一個具有粘性的廣播。它被發(fā)出去之后,會一直滯留在系統(tǒng)中,直到有與之匹配的接收者,才會將其發(fā)出去。

Sticky 廣播,使用 Context.sendStickyBroadcast() 方法進行發(fā)送廣播。

從文檔上可以看到,如果想要發(fā)送一個 Sticky 廣播,需要具有 BROADCAST_STICKY 權(quán)限,這個可以在 AndroidManifest.xml 中進行注冊,而如果沒有此權(quán)限,則會拋出 SecurityException 異常。

對于系統(tǒng)而言,只會保留最后一條 Sticky 廣播,并且會一直保留下去,也就是說,如果我們發(fā)送的 Sticky 廣播不被取消,當有一個接收者的時候就會收到它,再來一個還是能收到。所有我們需要在合適的實際,調(diào)用 removeStickyBoradcast() 方法,將其取消掉。

從上面的方法文檔中也可以看到 StickyBroadcast 已經(jīng)被標記為 @Deprecated ,出于一些安全的考慮,已經(jīng)將其標記為廢棄,不再推薦使用。我們作為開發(fā)者,對于一些被標記為 @Depracated 的方法,使用起來還是需要謹慎的。

4、本地廣播

前面介紹的廣播,都是全局的,只要被發(fā)出去之后,所有注冊了此廣播的 App ,都可以接受到它,這樣就帶來了安全的隱患。而有時候,我們只是想讓自己的 App 進程內(nèi)使用,而無需將廣播公布出去。那么就可以使用本地廣播。

本地廣播是 Android Support v4 : 21 版本才新增的廣播類型,它使用 LocalBroadcastManager (以下簡稱 LBM)類來管理。

LocalBroadcast 的使用非常的簡單,只需要將 Broadcast 的對應(yīng) API,替換為 LBM 為我們提供的 API 即可。

LBM 是一個單例對象,可以使用 LocalBroadcastManager.getInstance(Context ) 方法獲取到。在 Context 中定義的和 Broadcast 相關(guān)的方法,在 LBM 中都有對應(yīng)的 API 。非常有意思的是,LBM 為了區(qū)分異步和同步,使用了 sendBroadcast()sendBroadcastSync() 方法來做為區(qū)分。

三、注冊廣播接收者方式

在 Android 中 ,Broadcast 有兩種注冊方式:

  • AndroidManifest.xml 靜態(tài)注冊。
  • 代碼中動態(tài)注冊。

1、靜態(tài)注冊

在 AndroidManifest.xml 靜態(tài)注冊,是一種非常常用的注冊方式。


這里注冊了一個監(jiān)聽輸入法改變的系統(tǒng)廣播的 BroadcastReceiver。

2、動態(tài)注冊

有一些情況下,我們因為一些限制,會需要使用到動態(tài)注冊監(jiān)聽。

Context 中,為我們提供了動態(tài)注冊廣播接收者對應(yīng)的 api。


這幾行代碼和上面靜態(tài)注冊的效果是一樣的。但是既然是動態(tài)注冊,可以在需要的時候進行廣播接收者的注冊,那么在不需要的時候就需要對其進行取消。

動態(tài)取消廣播接收者的注冊,需要使用 Context.unregisterReceiver() 方法,它需要一個 BroadcastReceiver 對象作為參數(shù),這就是我們之前用于注冊的 Receiver 對象。

3、動態(tài)注冊和靜態(tài)注冊有什么區(qū)別?

理論上來說上來說,無論是使用靜態(tài)注冊,還是動態(tài)注冊,當這個廣播接收者被注冊上之后,他們的后續(xù)操作是一樣的。但是它們的注冊時機卻不同。

對于靜態(tài)注冊的接收者而言,實際上它在安裝到設(shè)備中之后,就已經(jīng)被注冊上了,只要有與它匹配的廣播被發(fā)出來,它就是可以被激活并處理廣播的,而對于動態(tài)注冊,只有當前 App 被啟動,并且執(zhí)行到 registerReceiver() 方法之后,才會完成注冊,才能接收匹配的廣播。

既然如此,Android 為了一些效率和安全的原因,規(guī)定一些系統(tǒng)廣播無法被靜態(tài)注冊,例如:SCREEN_ON、SCREEN_OFF、TIME_TICK 等,這種觸發(fā)頻率比較高的系統(tǒng)廣播。這些廣播只允許動態(tài)注冊,使用靜態(tài)注冊的方式雖然不會報錯,但是也不會有效。

4、BroadcastReceiver

無論是使用那種注冊方式,我們都需要有一個 BroadcastReceiver 對象,它是用于實際去處理廣播的對象,它是一個抽象類,需要實現(xiàn)其內(nèi)的方法 onReceive()。


使用 BroadcastReceiver 就可以接受到與之匹配的廣播,廣播是通過 IntentFilter 為過濾條件來匹配的,我們可以通過 onReceiver() 方法中的 intent 對象,來獲取到接收到的廣播的相關(guān)數(shù)據(jù)。

四、查缺補漏

到這里,基本上 Broadcast 的相關(guān)內(nèi)容就講解清楚了。但是實際使用中,有時候還是會碰到問題,這里單獨用一個小結(jié)來分析碰到的問題,有新的問題會持續(xù)更新。

1、被停止的 App 無法接收 Broadcast

對于 Broadcast 的 api ,在 Android api level 11 (Android 3.1)之后有過調(diào)整。新增了兩個 FLAG,用來控制 Broadcast 是否對處于停止狀態(tài)的 App 起作用。

這兩個 FLAG 為:

  • FLAG_INCLUDE_STOPPED_PACKAGES:表示包含未啟動的 App。
  • FLAG_EXCLUDE_STOPPED_PACKAGES:表示不包含未啟動的 App。

而加了這兩個 flag 的版本之后,系統(tǒng)會默認向所有 Broadcast 的 Intent 增加 FLAG_EXCLUDE_STOPPED_PACKAGES 這個 flag,這樣做是為了防止喚醒已經(jīng)被停止的 App 來處理這個廣播,這樣可以節(jié)約很多不必要的資源浪費??梢钥吹?,Android 為了優(yōu)化效率,一直是在做努力的,在 最新的 Android O 上也做了大的優(yōu)化。

而這樣導致如果 App 處于停止的狀態(tài)下,默認就不會接收到廣播的。那么有沒有辦法解決這個問題?如果廣播的發(fā)送方我們可以控制,只需要為廣播增加 FLAG_INCLUDE_STOPPED_PACKAGES 即可,如果沒發(fā)控制,暫時也沒什么好的辦法讓被停止的 App 接收到這部分廣播。

那么,我們還需要確定,什么情況下,App 會處于停止狀態(tài),現(xiàn)在能確定的就是兩種狀態(tài):

  • 首次安裝未啟動過。
  • 在任務(wù)管理器中,被『強行停止』后。

當然不排除有一些管理軟件會模擬『強行停止』的動作。

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

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

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