學習Android之廣播機制
廣播機制簡介
Android中的廣播機制十分靈活,每個應用程式都可以對自己感興趣的廣播進行註冊,這樣該程式就只會收到自己所關心的廣播內容,這些廣播可能是來自於系統的,也可能是來自於其他應用程式的。
Android提供了一套完整的API,允許應用程式自由地傳送和接收廣播。
傳送廣播可以藉助Intent,接收廣播則需要藉助BroadcastReceiver。
廣播的型別
標準廣播和有序廣播。
標準廣播:一種完全非同步執行的廣播,發出後,所有的BroadcastReceiver幾乎同時收到這條廣播訊息,沒有先後順序。效率高,但無法被截斷。
有序廣播:一種非同步執行的廣播,發出後,同一時刻只能有一個BroadcastReceiver收到訊息,當這個BroadcastReceiver處理完後,廣播才能繼續傳遞下去,有先後順序。優先順序高的BroadcastReceiver會先收到廣播訊息。並且還可以截斷正在傳遞的廣播。
接收系統廣播
Android內建了很多系統級別的廣播,比如手機開機,電量變化等等。想要接收他們的廣播,就要用到BroadcastReceiver。
動態註冊監聽時間變化
註冊方法一般有兩種:在程式碼中註冊(動態註冊)和在AndroidManifest.xml中註冊(靜態註冊)。
那麼如何建立一個BroadcastReceiver呢?
只需要新建一個類,繼承自BroadcastReceiver,並重寫父類的onReceive()方法即可。當有廣播來時,onReceive()方法就會執行。
例子:動態註冊一個監聽時間變化的程式。
在MainActivity中定義了一個內部類TimeChangeReceiver,繼承自BroadcastReceiver,重寫了onReceive()方法。
inner class TimeChangeReceiver : BroadcastReceiver() { override fun onReceive(p0: Context?, p1: Intent?) { TODO("Not yet implemented") } }
在onCreate()方法,先建立一個IntentFilter的例項,給它新增一個值為android.intent.action.TIME_TICK的action。因為系統時間發生變化時,系統發出的正是一條值為android.intent.action.TIME_TICK的廣播。也就是說,想要監聽什麼廣播,就在這裡新增相應的action。
接下來建立一個TimeChangeReceiver的例項,然後呼叫registerReceiver()方法進行註冊,將TimeChangeReceiver的例項和IntentFilter的例項都傳了進去,這樣TimeChangeReceiver就會收到所有值為android.intent.action.TIME_TICK的廣播,也就實現了監聽系統時間變化的功能。
val intentFilter = IntentFilter() intentFilter.addAction("android.intent.action.TIME_TICK") timeChangeReceiver = TimeChangeReceiver() registerReceiver(timeChangeReceiver,intentFilter)
這裡需要將timeChangeReceiver宣告為全域性變數,需要用到延遲初始化:
lateinit var timeChangeReceiver: TimeChangeReceiver
最後記得,動態註冊的BroadcastReceiver一定要取消註冊才行,在onDestory()中呼叫unregisterReceiver()方法即可。
unregisterReceiver(timeChangeReceiver)
最後執行程式,系統每隔一分鐘就會發出一條android.intent.action.TIME_TICK的廣播。
想要檢視完整的系統廣播列表,前往如下路徑:
<Android SDK>/platforms/<任意android api版本>/data/broadcast_actions.txt
靜態註冊實現開機啟動
動態註冊的可以自由地控制註冊與登出,在靈活性方面有很大的優勢。但是有一個缺點就是必須在程式啟動之後才能接收廣播。因為實在onCreate()中註冊的。
靜態註冊則能實現程式未啟動的情況下也能接收廣播。(簡述一下原理,在安裝應用的時候,系統會啟動PackageManagerService管理服務,這個服務對AndroidManifest進行解析,從而得到應用程式的相關資訊,比如service,activity,Broadcast等等。)
實現開機廣播的action值為android.intent.action.BOOT_COMPLETED。
首先我們需要新建一個廣播類,可以通過New->Other->Broadcast新建。新建時有兩個屬性:Exported屬性表示是否允許這個BroadcastReceiver接收本程式以外的廣播,Enabled屬性表示是否啟用這個BroadcastReceiver。都勾選上。
然後在onReceive()中編寫邏輯即可。
另外,靜態的BroadcastReceiver需要在AndroidManifest.xml檔案中註冊。不過新建時自動註冊了。
<receiver android:name=".BootCompleteReceiver" android:enabled="true" android:exported="true"></receiver>
同時,需要在<receiver>標籤中添加了一個<intent-filter>標籤,在裡面宣告相應的action。
<receiver android:name=".BootCompleteReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver>
最後再進行許可權宣告。
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
傳送自定義廣播
傳送標準廣播
1. 建立廣播接收者:在傳送廣播之前,需要先定義一個BroadcastReceiver來準備接收此廣播。新建一個MyBroadcastReceiver,並在onReceive()方法中編寫邏輯。
2. 新增廣播接收者需要接收的action:在AndroidManifest對這個BroadcastReceiver修改。將action設為com.example.broadcasttest.MY_BROADCAST。所以等會就會發送這樣一條廣播。
3. 傳送廣播:在MainActivity中的新增按鈕點選事件,裡面編寫廣播發送程式碼。
val intent = Intent("com.example.broadcasttest.MY_BROADCAST")
intent.setPackage(packageName)
sendBroadcast(intent)
為什麼會用到setPackage()方法:在Android 8.0系統之後,靜態註冊的BroadcastReceiver是無法接收隱式廣播的,而預設情況下我們發出的自定義廣播恰恰都是隱式廣播。因此這裡一定要呼叫setPackage()方法,指定這條廣播是傳送給哪個應用程式的,從而讓它變成一條顯式廣播,否則靜態註冊的BroadcastReceiver將無法接收到這條廣播。
傳送有序廣播
有序廣播是可以被截斷的。傳送有序廣播只需要改動一行程式碼。
sendOrderedBroadcast(intent,null)
此方法接收兩個引數:第一個Intent,第二個是一個與許可權相關的字串,這裡傳入null就行了。
這個時候的BroadcastReceiver是有先後順序的,而且前面的BroadcastReceiver還可以將廣播截斷,以阻止其繼續傳播。
設定先後順序是在註冊的時候進行設定,修改AndroidManifest.xml中的程式碼,如下所示:
<intent-filter android:priority="1"> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter>
通過android:priority屬性設定優先順序,值越大優先順序越高。
然後在onReceive()方法中呼叫abortBroadcast()方法,表示將這條廣播截斷,後面的BroadcastReceiver將無法再接收到這條廣播。
override fun onReceive(context: Context, intent: Intent) { TODO("BootCompleteReceiver.onReceive() is not implemented") abortBroadcast() }