在前邊幾篇關於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
在進程間使用。