在前邊幾篇關於Android系統兩個重要元件的介紹中,介面Activity
負責應用程式與使用者的互動,服務Service
負責應用程式內部執行緒間的互動或兩個應用程式程序之間的資料互動。看上去這兩大元件就能滿足日常應用程式的開發需求了,可是應用程式之間的互動,如果都使用服務Service
中的AIDL規範,那每個應用程式本身豈不是要宣告其他應用程式中的一些介面?這對兩個屬於不同開發者的應用程式來說很不友好。所以Android系統還提供了稱為廣播接收者BoradcastReceiver
的元件,採用廣播機制,以在兩個或多個未知應用程式間傳遞並處理通知。
Android系統的廣播機制,作為程序間通訊的一種方式,同樣可分為通訊訊息內容、傳送方、接收方三個方面。而廣播接收者BroadcastReceiver
元件,只是在接收方所使用的。為了更好的理解廣播接收者的使用方式,下面將按照程序間通訊的三個方面分別介紹。
完整的通訊訊息內容
與介面Activity
或者服務Service
類似,廣播間的通訊訊息內容也是以android.content.Intent意圖物件封裝起來的資料。在介面間互動中有對Intent
物件的使用說明。
隨意的廣播發送方
只要在任何有上下文環境android.content.Context物件的地方,都可以呼叫該物件的sendBroadcast()
系列方法傳送一條廣播,其使用方式也與啟動介面的startActivity()
系列方法或啟動服務的startService()
系列方法類似。
最常用的context.sendBroadcast(Intent intent)
方法,可以傳送一條隨機通知,在已經註冊的與其對應的廣播接收者中會收到當前通知傳送的 intent 內容。這裡如果有多個對應的廣播接收者,將會以隨機順序接收當前訊息內容。 其中 intent 引數就是對通訊的訊息內容封裝的例項化物件,其中必須呼叫intent.setAction(String action)
方法設定要傳送廣播的唯一標識行為,此處的 action 必須是系統內的唯一字串,以此作為接收該廣播的標誌,在下文的廣播接收者中同樣宣告相同字串的 action 來接收當前廣播內容。
也可以使用context.sendOrderedBroadcast (Intent intent, String receiverPermission)
方法,傳送一條按順序接收的通知,同樣在已經註冊的與其對應的廣播接收者中收到訊息內容,只不過當有多個對應的廣播接收者時,將會按照廣播接收者在註冊時的優先之 priority 屬性值的從大到小的順序依次接收,只有當兩個廣播接收者有相同大小的 priority 時才會隨機順序接收訊息內容。由於多個廣播接收者們是按順序接收的廣播,所以可以在其中某個廣播接收者中呼叫abortBroadcast()
中斷當前廣播,使其不會繼續向後面的廣播接收者傳遞。
另外,在官方推薦的androidx庫中,可以藉助androidx.localbroadcastmanager.content.LocalBroadcastManager本地廣播管理類,呼叫LocalBroadcastManager.getInstance(Context context).sendBroadcast(Intent intent)
系列方法,傳送一條只在當前應用程式程序中接收的廣播。這種廣播只是在同一個程序中使用,避免了多個程序間接收的冗餘衝突。
Android系統已經提供了一些 action 值標記的廣播,在一些常用的系統操作之後會發送廣播以通知其他應用程式。這些系統廣播以靜態常量的形式定義在android.content.Intent類中。例如當用戶修改飛航模式狀態時,系統會發送Intent.ACTION_AIRPLANE_MODE_CHANGED
值作為 action 的廣播;當用戶點亮螢幕時,系統會發送Intent.ACTION_SCREEN_ON
值作為 action 的廣播;當有新的應用程式被安裝後,系統會發送Intent.ACTION_PACKAGE_ADDED
值作為 action 的廣播 。。。
自定義的廣播接收者
作為廣播訊息接收並處理的主要元件,廣播接收者必須繼承自android.content.BroadcastReceiver類,同時實現該類的抽象方法onReceive (Context context, Intent intent)
。在onReceive()
方法中接收廣播訊息,其中的引數 context 是當前廣播接收者所在的上下文環境,引數 intent 則是接收到的廣播訊息的內容。
與介面
Activity
和服務Service
的回撥方法一樣,廣播接收者BroadcastReceiver
中的onReceive()
方法同樣是在系統UI主執行緒中被呼叫,因此該方法中同樣不能執行耗時操作。
如果其他應用程式所在程序每傳送一條廣播都回調廣播接收者的onReceive()
方法,對每一個廣播接收者來說都是繁瑣的處理過程。所以傳送的廣播 intent 中指定的 action 引數必須指定唯一標誌值,只有已經註冊過該 action 的廣播接收者,才會回撥onReceive()
方法。
在應用程式的目標版本為Android 8.0即API為26以前,廣播接收者的註冊方式有靜態註冊和動態註冊兩種,而從Android 8.0版本開始,除部分例外的廣播行為外,都只能採取動態註冊一種方式。
靜態註冊
廣播接收者BroadcastReceiver
的靜態註冊方式與介面Activity
、服務Service
的註冊方式類似,都是需要在清單檔案的<application></application>
標籤中聲明當前元件資訊。
廣播接收者的宣告使用<receiver></receiver>
標籤,在標籤下有android:name
屬性繫結自定義的廣播接收者.
而在<receiver></receiver>
標籤裡邊,同樣可以使用<intent-filter></intent-filter>
標籤作為意圖過濾。
在意圖過濾標籤中嵌入<action android:name=""/>
標籤以標記當前廣播接收者所繫結的系統唯一的 action 屬性值,該<action />
標籤可以有多個,只要傳送的 action 引數中有一個與其對應,就會呼叫當前廣播接收者的onReceiver()
方法。
對於靜態註冊的廣播接收者,系統在應用程式安裝之後就遍歷其註冊的廣播 action 值,從而當其他應用程式傳送對應 action 的廣播後,與之匹配,若匹配一致,則建立該廣播接收者的例項化物件,之後再呼叫廣播接收者的onReceiver()
方法,該方法執行結束後,當前的廣播接收者物件也就被銷燬了。當其他應用程式再次傳送對應的廣播後,同樣要再次建立新的廣播接收者的例項化物件並呼叫其onReceiver()
方法。這是廣播接收者BroadcastReceiver
與其他元件所不同的地方。
動態註冊
對於動態註冊的廣播接收者BroadcastReceiver
,可以在有上下文環境Context
物件的地方,通常是在介面Activity
或服務Service
中註冊的。
首先要呼叫自定義廣播接收者BroadcastReceiver
的構造方法建立其例項化物件。
其次還要藉助android.content.IntentFilter意圖過濾類建立其例項化物件。
通過IntentFilter
類的一參構造方法或該物件的setAction(String action)
方法,可以設定其 action 引數。
在需要註冊的位置,呼叫上下文環境Context
物件的registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
系列方法,可以將建立的自定義廣播接收者BroadcastReceiver
與設定了 action 引數的IntentFilter
物件繫結註冊。在該註冊邏輯之後,其他程序傳送的 action 廣播,才能在繫結的自定義廣播接收者中接收並呼叫其onReceiver()
方法。
在於註冊位置同級下不需要在處理接收廣播的位置,需要解除註冊,以降低系統消耗,呼叫上下文環境Context
物件的unregisterReceiver(BroadcastReceiver receiver)
方法,傳入之前註冊過的BroadcastReceiver
物件即可。
對於動態註冊的廣播接收者,通常需要在元件配對的生命週期方法中註冊與解除註冊,因此其生命週期也必然要小於註冊所在的元件。
Android系統提供的廣播機制,只需要知道廣播的 action 屬性值,就可以傳送或處理該條廣播。這不僅在不同程序之間,在同一個程序內使用也很方便,但是官方建議不要濫用廣播接收者,否則會導致系統變慢。一般開發過程中常用以建立系統廣播的廣播接收者BroadcastReceiver
為主,輔助以自定義廣播的廣播接收者BroadcastReceiver
在程序間使用。