本文為原創文章,歡迎轉載!轉載時請注明出處:http://blog.csdn.net/windskier
這篇文章開始從framework的角度來研究一下android四大控件最後一個控件BroadcastReceiver 的機制與原理,BroadcastReceiver 字面意思就是廣播接收器,它能接收來自于系統的以及其他appliaction的廣播消息,是android中非常重要的一個component。
1. BroadcastReceiver注冊
BroadcastReceiver的注冊有兩種方式,一種是通過Context.registerReceiver()方法來進行動态注冊,或者通過在AndroidManifest.xml中進行靜态注冊。AMS對兩種不同注冊方式的Receiver有不同的管理方式。靜态注冊就不用說了,靜态注冊的receiver的管理是有PMS負責的,而AMS則需要對動态注冊的receiver注冊和管理。下面就研究一下AMS對receiver的動态管理過程。
1.1 ReceiverDispatcher
同Service一樣,LoadedApk為receiver提供了一個ReceiverDispatcher類型,類似于Service管理時的ServiceDispatcher類型,對于每個要注冊的receiver,LoadedApk會為其建立一個ReceiverDispatcher。從其名稱來看,ReceiverDispatcher就是向receiver dispatch廣播資訊,下面簡單介紹一下這個類型存在的價值,廣播資訊是經由AMS進行分發管理的,是以需要為AMS向receive分發廣播消息設計一個接口,通過這個接口可以實作receiver與AMS的IPC通信,BroadcastReceiver 本身并不是一個IBinder接口,是以需要單獨設計一個,這個接口就是InnerReceiver,InnerReceiver一方面與AMS進行IPC通信,另一方面還需要與receiver進行互動,為了區分IPC的接口方法和與receiver的互動接口,這裡需要定義了另外一個類型ReceiverDispatcher,這就是ReceiverDispatcher産生的原因。 @ContextImpl.java
private Intent registerReceiverInternal(BroadcastReceiver receiver,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission);
} catch (RemoteException e) {
return null;
}
}
1.2 InnerReceiver
上面一節已經介紹了InnerReceiver存在的意義,它其實是一個IInterface,用于AMS和Receiver之間的IPC通信,InnerReceiver實作了接口IIntentReceiver。 @IIntentReceiver.aidl
oneway interface IIntentReceiver {
void performReceive(in Intent intent, int resultCode,
String data, in Bundle extras, boolean ordered, boolean sticky);
}
1.3 sticky broadcast
有這麼一種broadcast,在發送并經過AMS分發給對應的receiver後,這個broadcast并不會被丢棄,而是儲存在AMS中,當有新的需要動态注冊的receiver請求AMS注冊時,如果這個receiver能夠接收這個broadcast,那麼AMS會将在receiver注冊成功之後,馬上向receiver發送這個broadcast。這種broadcast我們稱之為stickybroadcast。 當receiver注冊成功後,AMS會将合适的sticky broadcast作為方法registerReceiver()傳回給Component,以便通知Component目前是否有sticky broadcast存在。
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
如果有多個符合receiver條件的 sticky broadcast的話,那麼AMS将會隻傳回一個時間較早的sticky broadcast,并且會将這些所有滿足要求的sticky broadcast均發送給reciever,交由receiver去處理。sticky broadcast的發送于取消通過方法sendStickyBroadcast()/sendStickyOrderedBroadcast() 以及removeStickyBroadcast()來控制。 registerReceiver()@ActivityManagerService.java
// Enqueue broadcasts for all existing stickies that match
// this filter.
if (allSticky != null) {
ArrayList receivers = new ArrayList();
receivers.add(bf);
int N = allSticky.size();
for (int i=0; i<N; i++) {
Intent intent = (Intent)allSticky.get(i);
BroadcastRecord r = new BroadcastRecord(intent, null,
null, -1, -1, null, receivers, null, 0, null, null,
false, true, true);
if (mParallelBroadcasts.size() == 0) {
scheduleBroadcastsLocked();
}
mParallelBroadcasts.add(r);
}
}
1.4 動态注冊receiver的注冊管理
receiver 的動态注冊過程就需要掌握以上幾個知識點就可以了,真正的注冊名其曰注冊,其實質就是将其交由 AMS 去管理,是以下面我們來分析研究一下 AMS 是通過哪些資料結構來管理 receiver 的。掌握了這些資料結構的含義以及它們之間的互相關系之後,我們就明白了整個需注冊 receiver 的注冊過程了。
1.4.1 BroadcastFilter
對于每個注冊過程,由兩部分組成,一部分是是BroadcastReceiver,另一部分是IntentFilter,BroadcastReceiver通過IntentFilter來指定要接收的Intent BroadCast。而BroadcastFilter則是繼承自IntentFilter,它記錄了目前需注冊的Receiver的IntentFilter資訊。是以每一組BroadcastReceiver和IntentFilter對應一次注冊過程,每個注冊過程即是一個BroadcastReceiver向AMS訓示一個Intent Broadcast的需求,如果有多個Intent Broadcast需求,BroadcastReceiver需要向AMS進行多次注冊。
1.4.2 ReceiverList
ReceiverList繼承自Arraylist,并且該Arraylist是以BroadcastFilter為元素,同時我們知道BroadcastReceiver的注冊過程的二要素是BroadcastReceiver和IntentFilter,對于同一個BroadcastReceiver可能會接收多個Intent Broadcast,是以每個IIntentReceiver可能會對應多個BroadcastFilter,這也是設計ReceiverList這個Arraylist的主要目的。
1.4.3 mReceiverResolver
mReceiverResolver的定義如下:
final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
= new IntentResolver<BroadcastFilter, BroadcastFilter>()
mReceiverResolver是一個IntentResolver類型的對象,通過它AMS可以将BroadcastFilter按照不同的元素,如action、data、category,進行管理,這樣的話在AMS收到broadcast之後,會根據這個broadcast的Intent的元素去query mReceiverResolver,擷取對應的BroadcastFilter。
既然mReceiverResolver實作了對BroadcastFilter的管理,那麼前面的ReceiverList類型存在的意義是什麼呢? mReceiverResolver和ReceiverList的關系其實并不沖突,AMS可以通過mReceiverResolver來獲得可以接收發來的Broadcast的所有的BroadcastFilter。通過上面的類型關系圖,我們可以看出BroadcastFilter中反向連結到自己所處的ReceiverList,看見兩者之間的關系并不是對立的,而是适用于AMS接收分發過程的不同階段。
1.4.4 BroadcastRecord
就向前面介紹的android中的其他幾大component一樣,BroadReceiver在被處理時同樣是通過一個BroadcastRecord的Record類型來進行管理的,BroadcastRecord隻有在AMS在處理分發Broadcast時才去建立,它記錄着每個Broadcast的資訊,在Receiver的過程并不涉及Broadcast。
2. Broadcast的發送過程
上面第一節介紹了Receiver的動态注冊過程,那麼接下來我們分析一下Broadcast的發送處理。
2.1 Broadcast的發送方式
Broadcast根據不同的需求有多種不同的發送方式,下面來分析一下具體有幾種發送方式。下面的表格列出了application幾種不同的發送方式以及不同方式對應的方法。
不同的發送方式 | 對應的方法 |
普通模式 | sendBroadcast(Intent intent) |
Broadcast sender 指定了receiver的permission,隻有符合這個permission的receiver才能接收這個Broadcast | sendBroadcast(Intent intent, String receiverPermission) |
要求符合要求的所有receiver (包括動态注冊和靜态注冊的receiver)按照優先級順序來接收這個Broadcast | sendOrderedBroadcast(Intent intent, String receiverPermission) |
某些情況下,Broadcast sender在發出Broadcast之後,向AMS指定一個resultReceiver,期望所有的receiver接收并處理完Broadcast之後,将在各個receiver間傳遞的resultExtras傳回給resultReceiver。這個resultExtras既可以由sender指定,也可以由某一個receiver指定,一般情況下隻要receiver需要向sender傳回資料,則可以通過這個resultExtras | sendOrderedBroadcast(Intent intent,String receiverPermission, BroadcastReceiver resultReceiver,Handler scheduler, int initialCode, String initialData,Bundle initialExtras) |
Sticky Broadcast。前面已描述 | sendStickyBroadcast(Intent intent); endStickyOrderedBroadcast(Intent intent,BroadcastReceiver resultReceiver,Handler scheduler, int initialCode, String initialData,Bundle initialExtras) |
2.2 Package manager Broadcast
AMS在處理收到的Broadcast時,需要對一些特殊的Broadcast做特殊處理,比如Package manager發來的關于Package移除、增加、修改等操作的,由于android系統允許user在package内部component在運作的情況下進行如上所述的操作,是以每當發生這些操作時,PM則會通過發送Broadcast的方式通知AMS進行屬于AMS範疇的一些操作,如當PM移除某個Package的時候,要求AMS停止這個Package相關的所有Component等。
同樣的,關于timezone的一些事件也是通過Broadcast的方式通知AMS,這裡就不再分析。
2.3 Protected Broadcast
2.3.1 Protected Broadcast作用
從其名稱看即是受保護的Broadcast,它聲明在AndroidManifest.xml的一級節點中,對應的節點名稱為<protected-broadcast>,一般情況下,protected broadcast定義的是系統級别的broadcast,通常隻能被系統服務或者appliaction發送,下面我們分析那些情況下才能發送protected broadcast。
首先看一下protectedbroadcast的定義,它的主要元素就是一個action name,這些action隻能被系統發送,我們可以想象一下,系統級别的broadcast可以不受限制的被所有的應用發送的後果,那将是非常嚴重的,不可想象的。是以從安全性的角度出發,android為了保證這些系統級别的broadcast不被第三方的應用濫發,提出了這個protected broadcast的概念。
<protected-broadcast
android:name=[action String] />
Android規定了一些可以發送protected broadcast的程序,
● android:sharedUserId="android.uid.system"
● android:sharedUserId="android.uid.phone"
● android:sharedUserId="android.uid.shell"
● 設定android:persistent的appliaction。
對于其他不符合上述四條的程序發送broadcast時,AMS回去檢查該是否是protected broadcast規定的action,如果是,那麼将不允許這個broadcast發送。具體代碼如下:
broadcastIntentLocked()@ActivityManagerService.java
if (callingUid == Process.SYSTEM_UID || callingUid == Process.PHONE_UID
|| callingUid == Process.SHELL_UID || callingUid == 0) {
// Always okay.
} else if (callerApp == null || !callerApp.persistent) {
try {
if (AppGlobals.getPackageManager().isProtectedBroadcast(
intent.getAction())) {
String msg = "Permission Denial: not allowed to send broadcast "
+ intent.getAction() + " from pid="
+ callingPid + ", uid=" + callingUid;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception", e);
return BROADCAST_SUCCESS;
}
}
2.3.2 Protected Broadcast定義
上面介紹了Protected Broadcast的作用,知道這類Broadcast一般是系統發送的,那麼我們再來分析一下哪些AndroidManifest.xml中可以定義Protected Broadcast。我們可容易的想到,Protected Broadcast也不是随随便便定義在一個application的AndroidManifest.xml。
隻有系統appliaction才能在其AndroidManifest.xml中定義Protected Broadcast,系統appliaction包括/system/framework、/system/app、vendor/app下的package,是以裝置中安裝的第三方apk中如果定義了Protected Broadcast,那麼這個Protected Broadcast将不生效。下面代碼是對<protected-broadcast>屬性的解析,可以看出解析之後會檢查目前的package是否為系統package,如果是則提取這個ProtectedBroadcast。
parsePackage ()@PackageParser.java
} else if (tagName.equals("protected-broadcast")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);
// Note: don't allow this value to be a reference to a resource
// that may change.
String name = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);
sa.recycle();
if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
if (pkg.protectedBroadcasts == null) {
pkg.protectedBroadcasts = new ArrayList<String>();
}
if (!pkg.protectedBroadcasts.contains(name)) {
pkg.protectedBroadcasts.add(name.intern());
}
}
XmlUtils.skipCurrentTag(parser);
}
2.4 查找receiver
這一節來分析一下AMS在收到Broadcast之後,查找比對的receiver來向其分發這個Broadcast。前面說過receiver有兩種,一種是動态注冊的,另一種則是預先定義靜态注冊的。不同類型的receiver有不同的管理方式,動态注冊的receiver從上面章節中介紹的IntentResolver mReceiverResolver中擷取;靜态注冊的receiver則是從PM中擷取。
2.5 Broadcast分發模式
2.1中介紹了幾種android中常用的Broadcast發送方式,sticky模式上面的文章中已經做了介紹,下面主要介紹一些ordered模式和非ordered模式下,不同的分發模式。
2.5.1 非ordered模式
預設情況下,Broadcast發送方式是非order模式的,這裡所說的ordered和非ordered模式是針對動态注冊的receiver來說的,而對于靜态注冊的receiver則一律是ordered模式。非ordered模式下,AMS對Broadcast的分發是一種非同步的模式,也就是說,不待一個receiver處理完Broadcast,就會将Broadcast分發給下一個receiver。
2.5.2 ordered模式
ordered模式下,動态注冊的receiver将會采取和靜态注冊的receiver相同的處理模式,類似于一種同步模式,AMS會等待一個reciever處理完這個Broadcast,才會将Broadcast分發給下一個receiver。這種模式下,receiver的處理有了先後順序,是以必須考慮如何布置這個先後順序,這裡按照優先級的順序來給receiver的處理順序進行排序。
對于動态注冊的receiver,可以在其IntentFilter類型中定義它的優先級;而對于靜态注冊的receiver,同樣的我們可以在其<intent-filter>屬性中定義它的優先級。根據receiver優先級從高到低進行排序,如果動态的注冊的和靜态注冊的receive處在相同的優先級,那麼将優先處理動态注冊的receiver。
broadcastIntentLocked()@ActivityManagerService.java
int NT = receivers != null ? receivers.size() : 0;
int it = 0;
ResolveInfo curt = null;
BroadcastFilter curr = null;
while (it < NT && ir < NR) {
if (curt == null) {
curt = (ResolveInfo)receivers.get(it);
}
if (curr == null) {
curr = registeredReceivers.get(ir);
}
if (curr.getPriority() >= curt.priority) {
// Insert this broadcast record into the final list.
receivers.add(it, curr);
ir++;
curr = null;
it++;
NT++;
} else {
// Skip to the next ResolveInfo in the final list.
it++;
curt = null;
}
}
}
while (ir < NR) {
if (receivers == null) {
receivers = new ArrayList();
}
receivers.add(registeredReceivers.get(ir));
ir++;
}
2.6 Broadcast分發過程
Broadcast分發過程是一個異步過程,AMS在收到Broadcast之後,會根據不同類型的receiver,按照Broadcast發送發指定的發送模式進行分發,現在我們來分析一下Broadcast分發過程。
AMS向自己的消息隊列發送消息BROADCAST_INTENT_MSG對所有的receiver進行分發操作,所有的分發操作由方法processNextBroadcast()來實作,是以這一部分我們着重分析一下這個方法中的實作過程。
2.6.1 非ordered分發
上面已經對ordered模式和非ordered模式進行了簡單介紹,這裡不再多介紹了。對于非ordered模式的分發過程,AMS則會将Broadcast直接分發給符合要求的receiver的程序的消息隊列,這種分發的方式可以視作一種異步方式,也可以看作是将該Broadcast同時分發給符合要求的動态注冊的receiver,上面章節中我們分析過隻有動态注冊的receiver才會有ordered和非ordered模式之分,而靜态注冊的reciever則無此之分,一概視為ordered模式。
2.6.2 ordered分發
滿足ordered模式的receiver,AMS對其進行Broadcast分發時是一種同步的過程,隻有前一個receiver處理完該Broadcast之後,并且receiver通知AMS該Broadcast已經處理結束,此時AMS才能将Broadcast分發給下一個receiver去處理,如下圖所示:
我們知道需要ordered分發的receiver既包括動态注冊的receiver,又包括靜态注冊的receiver,但是AMS向兩種不同的注冊方式的receiver分發Broadcast的方式有不盡相同,但是最基本的發送及處理過程是按照上面的圖示進行的,兩種注冊方式具體的分發和處理内容如下圖所示:
2.7 Broadcast處理逾時
AMS向各個BroadcastReceiver分發Broadcast過程以及BroadcastReceiver處理Broadcast的過程并不是無限期的,為了保證系統的響應性和穩定性,android給這個過程設定了固定期限,有Broadcast相關的有兩種固定期限,這兩種期限均是針對Ordered模式的Receiver,而對于非Ordered模式的Receiver則不需要考慮這個時限,下面來一一介紹一下。
2.7.1 BroadcastRecord處理總時長
針對每個BroadcastRecord的處理,均會設定一個總時長,這個總時長是指所有符合BroadcastRecord條件的receiver處理完這個Broadcast的總時長的上限。也就是說如果滿足條件的所有的Receiver均已經接收并處理完了這個Broadcast的時刻,不能超過規定的總時長,總時長跟滿足條件的Receiver總個數有關系,如下面代碼所示:
processNextBroadcast ()@ActivityManagerService.java
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2*BROADCAST_TIMEOUT*numReceivers))) {
Slog.w(TAG, "Hung broadcast discarded after timeout failure:"
+ " now=" + now
+ " dispatchTime=" + r.dispatchTime
+ " startTime=" + r.receiverTime
+ " intent=" + r.intent
+ " numReceivers=" + numReceivers
+ " nextReceiver=" + r.nextReceiver
+ " state=" + r.state);
broadcastTimeoutLocked(false); // forcibly finish this broadcast
forceReceive = true;
r.state = BroadcastRecord.IDLE;
}
}
2.7.2 BroadcastReceiver處理時長
除了設定針對一個BroadcastRecord的處理的總時長外,android針對每個Receiver的處理時長也做了一個時限設定,保證每一個的Receiver的處理時長不超過規定時長,具體如下面代碼所示:
processNextBroadcast ()@ActivityManagerService.java
if (! mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + BROADCAST_TIMEOUT;
if (DEBUG_BROADCAST) Slog.v(TAG,
"Submitting BROADCAST_TIMEOUT_MSG for " + r + " at " + timeoutTime);
setBroadcastTimeoutLocked(timeoutTime);
}
如果上述2種時長存在逾時的話,AMS将會抛出ANR, 向user報告目前的Receiver在處理Broadcast時存在無響應狀況或者響應時間過長等狀況。
broadcastTimeoutLocked()@ActivityManagerService.java
if (anrMessage != null) {
// Post the ANR to the handler since we do not want to process ANRs while
// potentially holding our lock.
mHandler.post(new AppNotResponding(app, anrMessage));
}