本文基于 AOSP android-9.0.0_r2
Android中的廣播機制是通過底層binder來傳遞Intent的跨進程通信方式。它是1-N
的機制,即一個廣播端可以與N個Receiver對應。
發送端為信息源, 它將一些信息封裝到Intent中,然后廣播出去。
接收端為信息消費端,它向系統注冊廣播Receiver(BroadcastReceiver與IntentFilter),當有對應(IntentFilter)廣播發生時,Receiver就會收到廣播并開始處理相應的信息。
Android的廣播機制涉及如下幾個方面
一. Receiver
1.1 靜態注冊
通過在AndroidManifest.xml里聲明注冊的廣播稱為靜態Receiver.
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="me.bobby.test2.MyReceiver"/>
</intent-filter>
</receiver>
如上所示,向系統注冊一個MyReceiver的廣播Receiver,對應的IntentFilter中的action為me.bobby.test2.MyReceiver
.
那么靜態注冊的Receiver是怎樣注冊到系統里的呢?
當安裝App或系統啟動時,PMS會對Apk中的AndroidManifest.xml進行解析。具體的代碼在PackageParser.java中的parseBaseApplication函數中
if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, true, false);
hasReceiverOrder |= (a.order != 0);
owner.receivers.add(a);
}
1.2 動態注冊
ContextImpl.registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
通過Activity里直接調用registerReceiver去注冊一個動態的廣播Receiver。
1.3 小結
從靜態廣播Receiver與動態廣播Receiver可以看出,
靜態廣播Receiver,并沒有刻意去注冊,只是由PMS在解析Apk時,將申明在AndroidManifest.xml里的廣播解析出來,并保存在PMS里僅此而已。
動態廣播Receiver,會在Activity的Context中直接調用接口往AMS中注冊,可以看出來BroadcastReceiver是直接注冊到LoadedApk中的mReceivers中的.
mReceivers的定義
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
mReceivers是一個ArrayMap, key為Context, 也就是一個Activity/Service/Provider這樣的實例, 它的value又是另一個ArrayMap, 這里的意思是一個Context其實是允許注冊多個BroadcastReceiver.
二. 發送端
2.1 發送廣播,找到match的Receiver,并加入到廣播隊列中
發送廣播流程, 主要是在AMS的broadcastIntentLocked中
- 1. 排除stopped的package
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
- 2. 檢查是否是protected的廣播
final boolean isProtectedBroadcast;
isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
if (!isCallerSystem) {
if (isProtectedBroadcast) {
throw new SecurityException(msg);
}
從PMS中查找該action是否是protected的廣播,如果是非系統App發出的protected廣播,直接拋出異常。
那什么是protected的廣播呢?
protected 廣播是由系統App在AndroidManifest.xml中定義的,比如 framework-res.apk里面定義了大多數protected broadcast.
而一般的apk可以申明protected broadcast么?答案是非系統apk可以定義protected廣播,但是不會有任何作用,盡管PMS能正常解析, 但是在applyPolicy中發現它非系統apk, 會重置為null.
3. sticky的廣播
見后面第三大節。4. 是否針對動態注冊的Receiver
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
// Need to resolve the intent to interested receivers...
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)== 0) {
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
如果Intent設置了FLAG_RECEIVER_REGISTERED_ONLY標志,表明該intent只會去找動態注冊的Receiver。 否則會查找靜態注冊和動態注冊的兩種Receiver。
其中 receivers list中存放的是靜態注冊的Receiver, collectReceiverComponents通過PMS的queryIntentReceivers去查詢靜態Receiver,說到底就是從1.1小節所構成的類圖中去查找。這個就不細說了。
而registeredReceivers里存放的是動態注冊的Receiver,也就是接下來所說的。
- 5. 查找動態注冊的Receiver
if (intent.getComponent() == null) {
if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
// Query one target user at a time, excluding shell-restricted users
for (int i = 0; i < users.length; i++) {
...
List<BroadcastFilter> registeredReceiversForUser =
mReceiverResolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, users[i]);
if (registeredReceivers == null) {
registeredReceivers = registeredReceiversForUser;
} else if (registeredReceiversForUser != null) {
registeredReceivers.addAll(registeredReceiversForUser);
}
}
} else {
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false /*defaultOnly*/, userId);
}
}
intent.getComponent()如果不為空時,則表明intent意圖很明顯,就是找某個特定的component, 所以如果它不為空時,則會依次遍歷去查找。其實說到底,就是從1.2小節所構成的類圖中查詢。查詢出來的廣播receiver放在registeredReceivers中。
- 6. enqueue無序的動態注冊的廣播
final boolean replacePending =
(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
...
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(...);
final boolean replaced = replacePending && (queue.replaceParallelBroadcastLocked(r) != null);
if (!replaced) {
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}
上面這代碼針對的是動態注冊的Receiver。
如果設置FLAG_RECEIVER_REPLACE_PENDING,且之前已經有相同的廣播,則只需要替換即可,不需要重新入廣播隊列。 否則就將該廣播加入到mParallelBroadcasts中去, 這個是無序廣播。
- 7合并動態和靜態Receiver
// Merge into one list.
int ir = 0;
if (receivers != null) {
// this decision.
String skipPackages[] = null;
...
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++;
}
上面的代碼根據Receiver的優先級,合并成一個 receivers.
- 8 將最后的廣播依次加入到有序廣播隊列中
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord( ... );
final BroadcastRecord oldRecord =
replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
if (oldRecord != null) {
// 替換了,fire CANCELED
if (oldRecord.resultTo != null) {
final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);
try {
oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
oldRecord.intent, Activity.RESULT_CANCELED, null, null, false, false, oldRecord.userId);
} catch (RemoteException e) {
}
}
} else {
//加入到有序廣播隊列中
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
} else {
...
}
- 9 小結
如圖所示,
如果是無序廣播。
那么動態注冊的廣播receiver全部會加入到 mParallelBroadcasts中,而靜態注冊的廣播receiver會按優先級保存到mOrderedBroadcasts中。
而如果是有序廣播,
那么靜態注冊的receiver和動態注冊的receiver會根據優先級都放入到mOrderedBroadcasts中。
2.2 處理隊列
如上面broadcastQueue所示.
廣播隊列分為兩種,
- mFgBroadcastQueue隊列
從全名來看,這是一個foreground的隊列,它的timeout時間是10s, 不允許delay broadcast - mBgBroadcastQueue隊列
這是一個background的隊列,timeout時間是60s,允許delay broadcast
而每個廣播隊列中又包含有序廣播列表和并發廣播列表。
廣播發送在system的binder線程中,但是真正處理廣播的是在ServiceThread線程中。
廣播有兩種,那如何選擇正確的廣播隊列的呢?
BroadcastQueue broadcastQueueForIntent(Intent intent) {
final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}
如果Intent里設置了FLAG_RECEIVER_FOREGROUND,則使用mFgBroadcastQueue, 否則使用mBgBroadcastQueue, 當設置FLAG_RECEIVER_FOREGROUND,則接收端的允許有foreground優先級。
然后根據是無序廣播
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
有序廣播
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
進行調度。
binder線程一般會調用 scheduleBroadcastsLocked 向servicethread線程發送BROADCAST_INTENT_MSG, 然后開始進程處理廣播的流程。
public void handleMessage(Message msg) {
switch (msg.what) {
case BROADCAST_INTENT_MSG: {
processNextBroadcast(true);
} break;
case BROADCAST_TIMEOUT_MSG: {
synchronized (mService) {
broadcastTimeoutLocked(true);
}
} break;
}
}
下面來看下processNextBroadcast函數
- 1. mParallelBroadcasts里保存的是無序的廣播,此時會將無序的廣播依次deliver給廣播receiver
// First, deliver any non-serialized broadcasts right away.
while (mParallelBroadcasts.size() > 0) {
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis(); //記錄當前dispatch時間
r.dispatchClockTime = System.currentTimeMillis();
final int N = r.receivers.size();
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
}
addBroadcastToHistoryLocked(r);
}
-
2. 針對 mPendingBroadcast
檢查是否有當前還有Broadcast沒有處理完,如果正在處理廣播的Receiver還沒有處理完,則等著它處理完。
if (mPendingBroadcast != null) {
boolean isDead;
if (mPendingBroadcast.curApp.pid > 0) {
synchronized (mService.mPidsSelfLocked) {
ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
isDead = proc == null || proc.crashing;
}
} else {
final ProcessRecord proc = mService.mProcessNames.get(
mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
isDead = proc == null || !proc.pendingStart;
}
if (!isDead) {
// It's still alive, so keep waiting
return;
} else {
mPendingBroadcast.state = BroadcastRecord.IDLE;
mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
mPendingBroadcast = null;
}
}
- 3. 遍歷有序廣播, 處理第一個廣播
do {
if (mOrderedBroadcasts.size() == 0) {
mService.scheduleAppGcsLocked();
if (looped) {
mService.updateOomAdjLocked();
}
//已經沒有廣播了,直接return
return;
}
r = mOrderedBroadcasts.get(0);
boolean forceReceive = false;
//當前廣播上掛著多少個廣播receiver, 下面檢查broadcast是否掛住了。
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
//如果broadcast消耗時間大于了2*mTimeoutPeriod*numReceivers,則強制finish.
//可以看出超時時間和當前掛在廣播上的receiver個數有關。
broadcastTimeoutLocked(false); // forcibly finish this broadcast
forceReceive = true;
r.state = BroadcastRecord.IDLE;
}
}
//如果當前廣播狀態不是IDLE的話,說明當前正有廣播還在處理,直接返回。
if (r.state != BroadcastRecord.IDLE) {
return;
}
//當前廣播已經沒有等待接收的receiver了,通過廣播發送端,
// 當前廣播被Receiver中止了,后面的Receiver不會再接收到該廣播了。
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
// No more receivers for this broadcast! Send the final
// result if requested...
if (r.resultTo != null) {
try {
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId);
r.resultTo = null;
} catch (RemoteException e) {
}
}
...
}
} while (r == null);
上面代碼有一處非常重要, 即r.resultAbort, 如果當前正在處理廣播的Receiver調用了abortBroadcast了,則該有序廣播就不會再繼續發了,直接中斷,進入下一個廣播處理。
-
4. 處理廣播
經過第3步后,找到了即將要處理的廣播
// Get the next receiver...
int recIdx = r.nextReceiver++; //當前要處理的廣播receiver 索引
r.receiverTime = SystemClock.uptimeMillis();
if (recIdx == 0) { //如果處理的是該廣播的第一個receiver, 則記錄它的dispatch時間。
r.dispatchTime = r.receiverTime;
r.dispatchClockTime = System.currentTimeMillis();
}
r.nextReceiver是指下一次要處理的廣播receiver, 如上面broadcastQueue所示,每一個廣播都會掛著一個list的receiver, 這時,nextReceiver就用來指明下一次處理哪一個。
final BroadcastOptions brOptions = r.options;
final Object nextReceiver = r.receivers.get(recIdx);
if (nextReceiver instanceof BroadcastFilter) {
BroadcastFilter filter = (BroadcastFilter)nextReceiver;
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
...
return;
}
由broadcastQueue圖所示,有序廣播隊列會掛著靜態注冊和動態注冊的廣播receiver. 而動態廣播receiver, 對應的是BroadcastFilter, 所以這里如果是動態receiver, 則直接deliver給動態receiver. 而沒有直接deliver給靜態receiver, 說明后面會對靜態的receiver有更多的限制
- 5. 靜態廣播receiver
// Hard case: need to instantiate the receiver, possibly
// starting its application process to host it.
// 由注釋可知,下面針對的是靜態廣播receiver, 有可能會啟動靜態廣播的進程
ResolveInfo info = (ResolveInfo)nextReceiver;
ComponentName component = new ComponentName( info.activityInfo.applicationInfo.packageName, info.activityInfo.name);
獲得靜態廣播receiver組件信息
然后根據receiver的信息來決定是否將broadcast deliver給該receiver
- 6. receiver權限檢查
代碼很長,就不帖了。比如Receiver中的Permission是否granted, 是否exported等等,具體直接看代碼吧。
而下面這個檢查比較關鍵,因為它涉及到在android高版本中雖然注冊了靜態廣播receiver, 但是有廣播發生時,Receiver確收不到廣播的情況
if (!skip) {
final int allowed = mService.getAppStartModeLocked(
info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
skip = true;
} else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
|| (r.intent.getComponent() == null
&& r.intent.getPackage() == null
&& ((r.intent.getFlags()
& Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
&& !isSignaturePerm(r.requiredPermissions))) {
mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
component.getPackageName());
skip = true;
}
}
}
通過getAppStartModeLocked去獲得APP START模式,
getAppStartModeLocked會調用如下代碼,其中 alwaysRestrict = true
final int startMode = (alwaysRestrict)
? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
: appServicesRestrictedInBackgroundLocked(uid, packageName,
在appRestrictedInBackgroundLocked函數中
int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Apps that target O+ are always subject to background check
if (packageTargetSdk >= Build.VERSION_CODES.O) {
return ActivityManager.APP_START_MODE_DELAYED_RIGID;
}
...
如果當前Android版本是Oreo及以后,那返回是非APP_START_MODE_DELAYED_RIGID,
所以會進入else if, 從else if的條件可以看出。
設置FLAG_RECEIVER_EXCLUDE_BACKGROUND, 表明要排除靜態receiver.
如果intent沒有指定特定的component, package, 以及FLAG_RECEIVER_INCLUDE_BACKGROUND, 以及signature權限相關,也會排除靜態receiver.
這也就是在Android O后面,靜態廣播不會再收到一些系統廣播的原因了。
if (skip) {
r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
r.receiver = null;
r.curFilter = null;
r.state = BroadcastRecord.IDLE;
r.manifestSkipCount++;
scheduleBroadcastsLocked();
return;
}
如果該廣播對于這些靜態的receiver最后是skip的話,直接return掉。
相反,如果Receiver可以接收的話,此時就會修改廣播狀態為APP_RECEIVE
r.manifestCount++;
r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
r.state = BroadcastRecord.APP_RECEIVE; //修改該廣播的狀態為APP_RECEIVE.
r.curComponent = component;
r.curReceiver = info.activityInfo;
如果receiver所在的進程已經啟動了,調用processCurBroadcastLocked著手處理該廣播receiver
// Is this receiver's application already running?
if (app != null && app.thread != null && !app.killed) {
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
processCurBroadcastLocked(r, app, skipOomAdj);
return;
}
...
}
如果receiver所在的進程還沒有啟動,則先開啟該進程,
if ((r.curApp=mService.startProcessLocked(...)) == null) {
finishReceiverLocked(r, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
r.state = BroadcastRecord.IDLE;
return;
}
2.2.1 AMS通知Receiver廣播到來
從上面2.2小節可知,deliverToRegisteredReceiverLocked將廣播deliver給動態注冊的廣播。它可以是在有序和無序廣播中被調用。
第一個階段依然是權限檢查,可以參考 2.2中第4步,基本一樣.
if (ordered) { //針對有序廣播
r.receiver = filter.receiverList.receiver.asBinder(); //找到receiver
r.curFilter = filter;
filter.receiverList.curBroadcast = r;
r.state = BroadcastRecord.CALL_IN_RECEIVE;
if (filter.receiverList.app != null) {
r.curApp = filter.receiverList.app;
filter.receiverList.app.curReceivers.add(r);
mService.updateOomAdjLocked(r.curApp, true);
}
}
try {
if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
if (ordered) {
skipReceiverLocked(r);
}
} else {
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
}
if (ordered) {
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
}
} catch (RemoteException e) {
if (ordered) {
r.receiver = null;
r.curFilter = null;
filter.receiverList.curBroadcast = null;
if (filter.receiverList.app != null) {
filter.receiverList.app.curReceivers.remove(r);
}
}
}
可以參考上面動態注冊廣播的圖,deliverToRegisteredReceiverLocked會找到IIntenReceiver.Proxy,然后調用scheduleRegisteredReceiver去通知對應Receiver廣播發生了。
2.2.2 有序廣播queue收到Receiver完成廣播處理
有序廣播的一個特點是,如果高優先級的Receiver處理了廣播,它有權abort該廣播,那后果是低優先級的Receiver就不會再接收到該廣播。
Receiver調用abortBroadcast
public final void abortBroadcast() {
mAbortBroadcast = true;
}
并且Receiver會調用finishReceiver通知BroadcastQueue完成廣播處理
public void sendFinished(IActivityManager am) {
synchronized (this) {
try {
if (mOrderedHint) {
am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
mAbortBroadcast, mFlags);
} else {
am.finishReceiver(mToken, 0, null, null, false, mFlags);
}
} catch (RemoteException ex) {
}
}
}
mOrderedHint表明該廣播是有序廣播,此時會向broadcastqueue傳遞相應的處理結果,以及mAbortBroadcast
AMS在收到finishReceiver后
public void finishReceiver(IBinder who, int resultCode, String resultData,
Bundle resultExtras, boolean resultAbort, int flags) {
final long origId = Binder.clearCallingIdentity();
try {
boolean doNext = false;
BroadcastRecord r;
synchronized(this) {
BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
? mFgBroadcastQueue : mBgBroadcastQueue;
r = queue.getMatchingOrderedReceiver(who);
if (r != null) {
doNext = r.queue.finishReceiverLocked(r, resultCode,
resultData, resultExtras, resultAbort, true);
}
if (doNext) {
r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true);
}
// updateOomAdjLocked() will be done here
trimApplicationsLocked();
}
}
在finishReceiverLocked,會將r.resultAbort = resultAbort, 此時可以參見2.2小節第3步,如果廣播abort了,直接跳到下一個廣播cycle.
三、sticky的廣播
sticky的廣播為粘性廣播,什么意思呢?
我們知道如果是一般的有序或是無序廣播,如果是廣播處理cycle中,所有的receiver已經處理完該廣播,則廣播隊列會將該條廣播給刪除掉,所以后面再注冊的Receiver是不會收到這條已經被刪除的廣播的。
而sticky廣播不一樣,如果廣播發送端指定一條廣播是sticky的廣播,則系統會一直記錄該sticky廣播,即使當時已經處理完sticky廣播的Receiver, sticky廣播也不會消失。當新注冊一個廣播receiver, AMS會立即向該receiver廣播該sticky廣播。
3.1 sticky廣播發送端
在broadcastIntentLocked函數中
if (checkPermission(android.Manifest.permission.BROADCAST_STICKY, callingPid, callingUid)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(msg);
}
if (requiredPermissions != null && requiredPermissions.length > 0) {
return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
}
if (intent.getComponent() != null) {
throw new SecurityException("Sticky broadcasts can't target a specific component");
}
if (userId != UserHandle.USER_ALL) {
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(UserHandle.USER_ALL);
if (stickies != null) {
ArrayList<Intent> list = stickies.get(intent.getAction());
if (list != null) {
int N = list.size();
int i;
for (i=0; i<N; i++) {
if (intent.filterEquals(list.get(i))) {
throw new IllegalArgumentException( "Sticky broadcast " + intent + " for user "
+ userId + " conflicts with existing global broadcast");
}
}
}
}
}
上面的代碼表示 sticky的廣播
- 獲得
android.permission.BROADCAST_STICKY
- sticky廣播不能 enforce 權限
- sticky廣播不能作用在某一個特殊的component上
- sticky廣播如果不是針對所有用戶的話,會檢查它是否和當前sticky的廣播發生沖突
如果sticky廣播通過了上面的檢查,那就將sticky的廣播保存到 mStickyBroadcasts中
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
if (stickies == null) {
stickies = new ArrayMap<>();
mStickyBroadcasts.put(userId, stickies);
}
ArrayList<Intent> list = stickies.get(intent.getAction());
if (list == null) {
list = new ArrayList<>();
stickies.put(intent.getAction(), list);
}
final int stickiesCount = list.size();
int i;
for (i = 0; i < stickiesCount; i++) {
if (intent.filterEquals(list.get(i))) {
// This sticky already exists, replace it.
list.set(i, new Intent(intent));
break;
}
}
if (i >= stickiesCount) {
list.add(new Intent(intent));
}
3.2 sticky廣播Receiver
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
int flags) {
...
while (actions.hasNext()) {
String action = actions.next();
for (int id : userIds) {
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
if (stickies != null) {
ArrayList<Intent> intents = stickies.get(action);
if (intents != null) {
if (stickyIntents == null) {
stickyIntents = new ArrayList<Intent>();
}
stickyIntents.addAll(intents);
}
}
}
}
}
ArrayList<Intent> allSticky = null;
if (stickyIntents != null) {
final ContentResolver resolver = mContext.getContentResolver();
// Look for any matching sticky broadcasts...
for (int i = 0, N = stickyIntents.size(); i < N; i++) {
Intent intent = stickyIntents.get(i);
if (filter.match(resolver, intent, true, TAG) >= 0) {
if (allSticky == null) {
allSticky = new ArrayList<Intent>();
}
allSticky.add(intent);
}
}
}
看看剛注冊的Receiver是否可以接收sticky的廣播
if (allSticky != null) {
ArrayList receivers = new ArrayList();
receivers.add(bf);
final int stickyCount = allSticky.size();
for (int i = 0; i < stickyCount; i++) {
Intent intent = allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, false, null, null, OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
}
return sticky;
}
}
將sticky的廣播加入到廣播隊列中,然后進入第二節的廣播處理cycle. Receiver就會收到該sticky的廣播了。
四、廣播 ANR
從有序廣播的流程可以看出,有序廣播是一個一個的將廣播deliver給廣播Receiver. 如果Receiver處理廣播的時間過長、或因為代碼錯誤導致死循環了, 那后面的Receiver將不會再得到調用了。這是系統不愿看到的, 所以應該會有個超時的機制。
從第2.2小節可以看出,兩種廣播隊列在初始化時,它們都有一個timeout時間,也就是超時時間,如果一個廣播Receiver超過了timeout時間還沒有處理完,此時廣播隊列就會觸發超時機制觸發ANR
在 processNextBroadcastLocked 找到要處理的有序廣播后,也就意味著要deliver了,在deliver之前,會設置超時時間,
r.receiverTime = SystemClock.uptimeMillis();
...
if (! mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mTimeoutPeriod;
setBroadcastTimeoutLocked(timeoutTime);
}
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (! mPendingBroadcastTimeoutMessage) {
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
}
}
使用的機制是handler在mTimeoutPeriod后發送一條BROADCAST_TIMEOUT_MSG信息,如果在超時之前完成了,則會cancelBroadcastTimeoutLocked cancel掉該信息。
如果超時后會調用
final void broadcastTimeoutLocked(boolean fromMsg) {
...
//調度下一個receiver
// Move on to the next receiver.
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
//觸發ANR
if (!debugging && anrMessage != null) {
mHandler.post(new AppNotResponding(app, anrMessage));
}
}
五、小結
Android的廣播Receiver注冊方式包括
- 靜態注冊
- 動態注冊
Android的廣播發送分為
無序廣播 - sendBroadcast
有序廣播 - sendOrderedBroadcast
有序廣播會根據Receiver的優先級依次調度Receiver, 如果高優先級的Receiver完成廣播的處理后,調用了abortBroadcast, 則低優先級的Receiver將不會再收到該Broadcast了。sticky的廣播 - sendStickyBroadcast
需要獲得BROADCAST_STICKY權限,且sticky廣播不能指定一個特定的component上。
只有新注冊的動態廣播receiver才能接收到sticky的廣播,靜態receiver不能收到sticky的廣播。見2.2第6步分析。
AMS提供了兩種廣播隊列,一種是background,一種是foreground的隊列。根據廣播中intent中的flag,即是否設置FLAG_RECEIVER_FOREGROUND來決定是將broadcast放入哪個隊列中。
如果是foreground隊列中的廣播,這會提升廣播receiver的優先級到foreground, 至于是怎么提升的,請參考Android Low memory killer
computeOomAdjLocked 函數中
else if (isReceivingBroadcastLocked(app, mTmpBroadcastQueue)) {
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = (mTmpBroadcastQueue.contains(mFgBroadcastQueue))
? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
app.adjType = "broadcast";
procState = ActivityManager.PROCESS_STATE_RECEIVER;
}
isReceivingBroadcastLocked計算當前app的正在running的receivers是在哪些廣播隊列中(也就兩種,fg和bg)調度的,并放在mTmpBroadcastQueue中。 如果其中有從mFgBroadcastQueue調度的話,它的schedGroup會提升至SCHED_GROUP_DEFAULT , 否則SCHED_GROUP_BACKGROUND.
廣播ANR,廣播隊列中針對有序廣播會做超時處理,如果一個Receiver在timeout時間內還沒有處理完,此時會觸發ANR.
foreground的超時是10s
background的超時是60s.
還有一種本地廣播.
它其實就是一種進程內通信的方式,它不會與System進程通信,這樣就保證它更快,更安全。