1.1. 廣播簡介
Android 廣播與生活中的廣播概念不同,它是指系統中產生事件後的通知。Android 廣播不關心接收者是否收到處理或者如何處理廣播,可以說是一種單向的通知。
Android 通過 BraodcastReceiver 來監聽系統發出的廣播,不同的 BraodcastReceiver 通過設置不同的 fliter 來區分監聽廣播的類型。有些廣播的監聽需要相應的權限。
1.2. 注冊廣播
BraodcastReceiver 必須經過注冊才能具有監聽功能,注冊的方式有兩種:
1.2.1. 靜態注冊
通過在 AndroidManifest.xml 中注冊對應的 receiver,並設置要監聽的 action
其中 MyReceiver 為繼承 BroadcastReceiver 的類,重寫了 onReceiver 方法,並在 onReceiver方法中對廣播進行處理。
標簽設置過濾器,接收指定 action 廣播。靜態注冊方式的特點:不管改應用程序是否處於活動狀態,都會進行監聽。action 可以為自定義 action,也可以是系統自帶的 action。
1.2.2. 動態注冊
動態注冊是在程序中對 BroadcastReceiver 完成監聽注冊
MyReceiver receiver = new MyReceiver();
//創建過濾器,並指定 action,使之用於接收同 action 的廣播
IntentFilter filter = new IntentFilter("MyReceiver_Action");
//注冊廣播接收器
registerReceiver(receiver, filter);
在 activity 中注冊廣播,必須在 activity 結束時注銷廣播,一般在 onStart 中注冊
BroadcastReceiver,在 onStop 中取消 BroadcastReceiver,廣播接收器跟隨 Activity 的生命周期
//注銷廣播接收器
unregisterReceiver(receiver);
1.3. 生命周期
BroadcastReceiver 生命周期只有 8 秒左右,如果在 onReceive()內做超過 8 秒內的事情,就會報ANR(Application Not Response)程序無響應的錯誤信息。它的生命周期為從回調 onReceive()方法開始到該方法返回結果後結束。因此,如果要在收到廣播後做耗時操作,最好放到 service 中去做,子線程也不好,當子線程未結束的時候,BroadcastReceiver 已經銷毀。
1.4. 發送廣播
1.4.1. Android 中發送廣播的三種形式
廣播被分為三種不同的類型:“普通廣播(Normal broadcasts)”、“有序廣播(Ordered broadcasts)”和“粘性廣播(Sticky broadcast)”。普通廣播是完全異步的,可以在同一時刻(邏輯上)被所有廣播接收者接收到,消息傳遞的效率比較高,但缺點是:接收者不能將處理結果傳遞給下一個接收者,並且無法終止廣播 Intent 的傳播;然而有序廣播是按照接收者聲明的優先級別(聲明在intent-filter 元素的 android:priority 屬性中,數越大優先級別越高,取值范圍:-1000 到 1000(其實最大可以為 int 最大值即:2147483647)。也可以調用 IntentFilter 對象的 setPriority()進行設置),被接收者依次接收廣播。如:A 的級別高於 B,B 的級別高於 C,那麼,廣播先傳給 A,再傳給 B,最後傳給 C。A 得到廣播後,可以往廣播裡存入數據,當廣播傳給 B 時,B 可以從廣播中得到 A 存入的數據。Context.sendBroadcast()發送的是普通廣播,所有訂閱者都有機會獲得並進行處理。Context.sendOrderedBroadcast()發送的是有序廣播,系統會根據接收者聲明的優先級別按順序逐個執行接收者,前面的接收者有權終止廣播。BroadcastReceiver.abortBroadcast()如果廣播被前面的接收者終止,後面的接收者就再也無法獲取到廣播。對於有序廣播,前面的接收者可以將處理結果存放進廣播 Intent,然後傳給下一個接收者。Context.sendStickyBroadcast()是發送粘性廣播,使用這個 api需要權限 android.Manifest.permission.BROADCAST_STICKY,粘性廣播的特點是 Intent 會一直保留到廣播事件結束,而這種廣播也沒有所謂的 8 秒限制,即上文所講,如果 onReceive 方法執行時間太長,超過 8 秒的時候系統會將這個廣播置為可以干掉的 candidate,一旦系統資源不夠的時候,就會干掉這個廣播而讓它不執行。
1.5. 廣播優先級
1.5.1. 基本原則
接收無序廣播的接收器接收到廣播的順序是有序的(由優先級決定順序)
接收無序廣播的接收器也一樣可以設置優先級的
動態注冊廣播優先級高於靜態注冊廣播
同等優先級的動態接收器,先注冊的先接收
同等優先級的靜態接收器,接收廣播的順序與 String[] java.io.File.list()順序一致
Ps:這裡有一點需要注意的是,同等優先級的靜態接收器的接收順序具有不確定性,原因就是File.list()的方法返回的順序具有不確定性,如果需要查看某接收器的接收順序,最好是試驗大量的 apk 名。
1.5.2. ordered 廣播
假設有如下優先級的 5 個接收器
1.動態 A(優先級=1)
2.動態 B(優先級=2)
3.動態 C(優先級=2)
4.靜態 D(優先級=1)
5.靜態 E(優先級=2)
並且 B 先於 C 注冊
那麼實際接收順序應為
B C E A D
也就是說,如果靜態接收器的優先級高於動態接收器的優先級,那麼還是靜態接收器先接收到廣播(比如接收 SMS 廣播)
1.5.3. 非 ordered 廣播
動態接收器高優先級>動態接收器低優先級>靜態接收器高優先級>靜態接收器低優先級
1.6. 只能動態接受廣播源碼分析
有些廣播,我們無法用靜態接收器接收,比如 ACTION_SCREEN_ON,當屏幕被點亮的時候系統發送此廣播。
1. void com.android.server.PowerManagerService.initInThread()
2. Java 代碼
3. void initInThread() {
4. ??
5. mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
6. mScreenOnIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
7. mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
8. mScreenOffIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
9. ??
10. }
Intent 中都設置了 Intent.FLAG_RECEIVER_REGISTERED_ONLY,所以,如果要接收,必須動態注冊廣播接收器 ACTION_SCREEN_OFF 也是如此。
1.6.1. 關於 FLAG_RECEIVER_REGISTERED_ONLY 的說明
public static final int
FLAG_RECEIVER_REGISTERED_ONLY
Added in API level 1
If set, when sending a broadcast only registered receivers will be called -- no
BroadcastReceiver components will be launched.
Constant Value: 1073741824 (0x40000000)
ACTION_BATTERY_CHANGED(電池電量發生變化的時候,系統發送此廣播)該廣播就是這樣
void com.android.server.BatteryService.sendIntent()
Java 代碼
1. private final void sendIntent() {
2. //
3. Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
4. intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
5.
Pack up the values and broadcast them to everyone
6.
7. }
1.7. 廣播注冊過程分析
1.7.1. 靜態注冊 Receiver 的流程
靜態 receiver 的注冊是由 PackageManagerService 開機的時候負責初始 PMS 在開機的時候會對系統一些目錄逐個掃描,解析 apk 文件。靜態廣播接收器就是在 PMS 做這件事情的時候順便處理的。
PMS 會解析 apk 的 manifest 文件,查找這裡注冊的 receiver,然後加載到內存中。
PMS 初始化掃描目錄的順序:
system/framework
system/app
vendor/app
data/appd
rm/app-private
我們看到了 PMS 如何在初始化的時候如何解析 manifest 並把其中的 element 存放到內存中的其中receiver 保存到了 owner 的成員變量 receivers 中,owner 的類型是
android.content.pm.PackageParser.Package 也就是說 scanPackageLI 返回結果就是已經包含了manifest 信息的 Package 對象。
1.7.2. 動態注冊 Receiver 的流程
動態注冊最終會調用到 AMS 中的 registerReceiver 函數,最終所有動態注冊的 receiver 都保存到 AMS 的成員變量 mReceiverResolver 中。靜態廣播和動態廣播如何注冊的,我們已經全部分析完了。靜態廣播是 PackageManagerService負責,保存到其成員變量 mReceivers 中,動態廣播是 ActivityManagerService 負責,保存到其成員變量 mReceiverResolver 中。
1.8. 廣播發送過程分析
1.8.1. 分析
Context 中的 sendBroadCast 函數的實現是在 ContextImpl 中,和發送廣播相關的有如下六個函數:
void android.app.ContextImpl.sendBroadcast(Intent intent)
void android.app.ContextImpl.sendBroadcast(Intent intent, String receiverPermission)
void android.app.ContextImpl.sendOrderedBroadcast(Intent intent, String receiverPermission)
void android.app.ContextImpl.sendOrderedBroadcast(Intent intent, String receiverPermission,
BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData,
Bundle initialExtras)
void android.app.ContextImpl.sendStickyBroadcast(Intent intent)
void android.app.ContextImpl.sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver
resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle
initialExtras)
可以分為 3 組:1 普通廣播;2 Ordered 廣播;3 Sticky 廣播。不論哪種,最後都會由 AMS 處理 如果是非 ordered 廣播,那麼 mParallelBroadcasts 將存儲所有動態接收器,然後合並的時候,mParallelBroadcasts 設置為 null,所以不會合並到 receivers 中,如果是 ordered 廣播,那麼mParallelBroadcasts 將合並到 receivers 中,然後,不管是哪種廣播,最後都是調用scheduleBroadcastsLocked 繼續處理,最終到 processNextBroadcast 函數上。廣播是否有序,即通過 Boolean 變量 ordered 進行設置。
1.8.2. 總結
發送過程情況分為兩種(scheduleBroadcastsLocked),ordered 廣播和非 ordered 廣播、非ordered 廣播。先處理動接收器,然後處理靜態接收器 ordered 廣播同時處理動態接收器和靜態接收器先將動態接收器與靜態接收器合並,保持著與優先級相同的順序,優先級高的在前面,否則順序不變。靜態接收器與動態接收器優先級相同的話,動態接收器在前。