這篇文章緊接著上篇分析廣播的發送過程,發送廣播都是調用ContextImpl的接口去實現的,總共有二十多個,最終都是調用到AMS的broadcastIntent。主要分成下面九小節來說明。
1、設置Flag
2、檢查BroadcastOptions
3、當前是否有權力發出廣播
4、處理系統相關廣播
5、處理粘性廣播
6、registeredReceivers和receivers查詢
7、處理并行廣播
8、整理兩個receiver列表
9、處理串行廣播
1、上層調用sendBroadcast發送廣播
@Override
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
// 準備離開應用程序進程,進入SysfangfatemServer進程
intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
省去跨進程的過程,進入AMS的broadcastIntent方法
public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
String[] requiredPermissions, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
enforceNotIsolatedCaller("broadcastIntent");
synchronized(this) {
intent = verifyBroadcastLocked(intent);
//從mLruProcesses列表中查詢到進程
final ProcessRecord callerApp = getRecordForAppLocked(caller);
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
int res = broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null,
intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, appOp, bOptions, serialized, sticky,
callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
2、broadcastIntentLocked中處理廣播
broadcastIntentLocked的方法接近600行代碼,需要分段閱讀,才比較清楚。
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
//設置Flag
//檢查BroadcastOptions
//當前是否有權力發出廣播
//處理系統相關廣播
//處理粘性廣播
//registeredReceivers和receivers查詢
// 處理并行廣播
//整理兩個receiver列表
// 處理串行廣播
}
2.1、設置Flag
intent = new Intent(intent);
// 設置這個flag后,廣播不會發送給已經停止的應用,看來系統默認是不讓我們的廣播發送給已經停止的應用的
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
//如果AMS還沒有啟動好,不允許啟動一個新的進程
if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
//這個flag表示只有動態注冊的廣播接收者能收到廣播,如果你錯誤的設置了這個標記,廣播又是靜態注冊的,那么就收不到廣播
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
// 當不是USER_ALL廣播且當前用戶不是運行狀態,除非是系統升級廣播或者關機廣播,否則直接返回
if (userId != UserHandle.USER_ALL && !mUserController.isUserRunningLocked(userId, 0)) {
if ((callingUid != Process.SYSTEM_UID
|| (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
&& !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
Slog.w(TAG, "Skipping broadcast of " + intent
+ ": user " + userId + " is stopped");
return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
}
}
Intent中有兩個FLAG,FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES,表示intent是否要激活“處于停止狀態的”應用,如果確定要激活“處于停止狀態的”應用,那么Intent add FLAG_INCLUDE_STOPPED_PACKAGES就行了。
/**
* If set, this intent will not match any components in packages that
* are currently stopped. If this is not set, then the default behavior
* is to include such applications in the result.
*/
public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 0x00000010;
/**
* If set, this intent will always match any components in packages that
* are currently stopped. This is the default behavior when
* {@link #FLAG_EXCLUDE_STOPPED_PACKAGES} is not set. If both of these
* flags are set, this one wins (it allows overriding of exclude for
* places where the framework may automatically set the exclude flag).
*/
public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020;
2.2、檢查BroadcastOptions
BroadcastOptions brOptions = null;
if (bOptions != null) {
brOptions = new BroadcastOptions(bOptions);
if (brOptions.getTemporaryAppWhitelistDuration() > 0) {
// See if the caller is allowed to do this. Note we are checking against
// the actual real caller (not whoever provided the operation as say a
// PendingIntent), because that who is actually supplied the arguments.
if (checkComponentPermission(
android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
Binder.getCallingPid(), Binder.getCallingUid(), -1, true)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: " + intent.getAction()
+ " broadcast from " + callerPackage + " (pid=" + callingPid
+ ", uid=" + callingUid + ")"
+ " requires "
+ android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
}
}
2.3、當前是否有權力發出廣播
final String action = intent.getAction();
final boolean isProtectedBroadcast;
try {
//isProtectedBroadcast為true則代表該廣播在Framework/base/core/res/AndroidManifest.xml中有聲明為保護廣播,這樣的廣播只能由系統發出
isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception", e);
return ActivityManager.BROADCAST_SUCCESS;
}
final boolean isCallerSystem;
switch (UserHandle.getAppId(callingUid)) {
case Process.ROOT_UID:
case Process.SYSTEM_UID:
case Process.PHONE_UID:
case Process.BLUETOOTH_UID:
case Process.NFC_UID:
//以上進程都有權限發送廣播
isCallerSystem = true;
break;
default:
isCallerSystem = (callerApp != null) && callerApp.persistent;
break;
}
// First line security check before anything else: stop non-system apps from
// sending protected broadcasts.
if (!isCallerSystem) {
if (isProtectedBroadcast) {
String msg = "Permission Denial: not allowed to send broadcast "
+ action + " from pid="
+ callingPid + ", uid=" + callingUid;
Slog.w(TAG, msg);
throw new SecurityException(msg);
} else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
// Special case for compatibility: we don't want apps to send this,
// but historically it has not been protected and apps may be using it
// to poke their own app widget. So, instead of making it protected,
// just limit it to the caller.
if (callerPackage == null) {
String msg = "Permission Denial: not allowed to send broadcast "
+ action + " from unknown caller.";
Slog.w(TAG, msg);
throw new SecurityException(msg);
} else if (intent.getComponent() != null) {
// They are good enough to send to an explicit component... verify
// it is being sent to the calling app.
if (!intent.getComponent().getPackageName().equals(
callerPackage)) {
String msg = "Permission Denial: not allowed to send broadcast "
+ action + " to "
+ intent.getComponent().getPackageName() + " from "
+ callerPackage;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
} else {
// Limit broadcast to their own package.
intent.setPackage(callerPackage);
}
}
}
保護性廣播是什么呢,frameworks/base/core/res/AndroidManifest.xml文件中可以看到定義,這些定義的廣播都是保護廣播,只能由系統發送,如果有不具有系統權限的應用試圖發送系統中的“保護性廣播”,那么到AMS的broadcastIntentLocked()處就會被攔住,AMS會拋出異常,提示"Permission Denial: not allowed to send broadcast"。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android" coreApp="true" android:sharedUserId="android.uid.system"
android:sharedUserLabel="@string/android_system_label">
<!-- ================================================ -->
<!-- Special broadcasts that only the system can send -->
<!-- ================================================ -->
<eat-comment />
<protected-broadcast android:name="android.net.tether.CONNECTEDSTA_CHANGE" />
<protected-broadcast android:name="android.intent.action.SCREEN_OFF" />
<protected-broadcast android:name="android.intent.action.SCREEN_ON" />
...
<protected-broadcast android:name="android.intent.action.PRE_BOOT_COMPLETED" />
...
</manifest>
2.4、處理系統相關廣播
if (action != null) {
switch (action) {
case Intent.ACTION_UID_REMOVED://uid移除
case Intent.ACTION_PACKAGE_REMOVED://package移除
case Intent.ACTION_PACKAGE_CHANGED://package改變
case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE: app正在移動到SD卡中,發出的廣播
case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE: app完成移動到SD的操作,發出的廣播
case Intent.ACTION_PACKAGES_SUSPENDED:
case Intent.ACTION_PACKAGES_UNSUSPENDED:
case Intent.ACTION_PACKAGE_REPLACED://替換一個現有的安裝包時發出的廣播(不管現在安裝的APP比之前的新還是舊,都會發出此廣播)
case Intent.ACTION_PACKAGE_ADDED: //增加package
case Intent.ACTION_PACKAGE_DATA_CLEARED:
case Intent.ACTION_TIMEZONE_CHANGED://時區改變
case Intent.ACTION_TIME_CHANGED://時間改變
case Intent.ACTION_CLEAR_DNS_CACHE://DNS緩存清空
case Proxy.PROXY_CHANGE_ACTION://網絡代理改變
case android.hardware.Camera.ACTION_NEW_PICTURE:
case android.hardware.Camera.ACTION_NEW_VIDEO:
}
}
還有更多系統廣播
2.5、處理粘性廣播
if (sticky) {
//需要android.Manifest.permission.BROADCAST_STICKY才能發送粘性廣播
if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
callingPid, callingUid)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid="
+ callingPid + ", uid=" + callingUid
+ " requires " + android.Manifest.permission.BROADCAST_STICKY;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
//發送粘性廣播不能強制添加別的權限
if (requiredPermissions != null && requiredPermissions.length > 0) {
Slog.w(TAG, "Can't broadcast sticky intent " + intent
+ " and enforce permissions " + Arrays.toString(requiredPermissions));
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) {
//根據廣播類型,取出stickies
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
UserHandle.USER_ALL);
if (stickies != null) {
//廣播是使用Intent描述的,用action取出廣播列表
ArrayList<Intent> list = stickies.get(intent.getAction());
if (list != null) {
int N = list.size();
int i;
for (i=0; i<N; i++) {
//粘性廣播發送后是會保存下來的,故如果已經存在則不需要重新發送 ,
// filterEquals函數會比較兩個intent的action、data、type、class以及categories等信息,
if (intent.filterEquals(list.get(i))) {
throw new IllegalArgumentException(
"Sticky broadcast " + intent + " for user "
+ userId + " conflicts with existing global broadcast");
}
}
}
}
}
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))) {
// 新發送的intent在ArrayList中已經有個“相等的”舊intent時,則會用新的替掉舊的
list.set(i, new Intent(intent));
break;
}
}
if (i >= stickiesCount) {
list.add(new Intent(intent));
}
}
sticky為true,表示是粘性廣播,發送粘性廣播,一定要有android.Manifest.permission.BROADCAST_STICKY權限,沒有的話就拋出SecurityException,Permission Denial: broadcastIntent() requesting a sticky broadcast...。在AMS中,所有相同的粘性廣播都被保存在一個List中,這些List最終被保存在AMS成員變量mStickyBroadcasts中, mStickyBroadcasts的定義是這樣的: final HashMap<String, ArrayList<Intent>> mStickyBroadcasts = new HashMap<String, ArrayList<Intent>>();注意粘性廣播是在注冊的時候加入到廣播隊列并且處理的,請移步Android源碼解析---廣播的注冊過程
2.6、registeredReceivers和receivers查詢
為了合理處理“串行廣播”和“并行廣播”,broadcastIntentLocked()方法中搞出了兩個list,一個是receivers,另一個是registeredReceivers,registeredReceivers是動態廣播接收器列表 ,receivers是靜態廣播接收器列表 。
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
// Need to resolve the intent to interested receivers...
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
//collectReceiverComponents內部調用包管理器的queryIntentReceivers()接口,查詢出和intent匹配的所有靜態receivers,此時所返回的查詢結果本身已經排好序了,因此,該返回值被直接賦值給了receivers變量
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
if (intent.getComponent() == null) {
if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
// Query one target user at a time, excluding shell-restricted users
for (int i = 0; i < users.length; i++) {
if (mUserController.hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
continue;
}
//此時返回的registeredReceivers中的子項是沒有經過排序的,在后面queue.scheduleBroadcastsLocked()會被處理掉
List<BroadcastFilter> registeredReceiversForUser =
mReceiverResolver.queryIntent(intent,
resolvedType, false, users[i]);
if (registeredReceivers == null) {
registeredReceivers = registeredReceiversForUser;
} else if (registeredReceiversForUser != null) {
registeredReceivers.addAll(registeredReceiversForUser);
}
}
} else {
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false, userId);
}
}
2.7 處理并行廣播
上面已經獲取了并行廣播和串行廣播,現在現將并行廣播給處理掉
final boolean replacePending =
(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueing broadcast: " + intent.getAction()
+ " replacePending=" + replacePending);
//并行廣播列表大小
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
//參數ordered標記當前發送的廣播是否是有序廣播,可以看到如果發送的是無序廣播,進入的是并行廣播隊列
if (!ordered && NR > 0) {
// 如果不是有序廣播, 不用等待目標組件是否啟動,就可以發送
if (isCallerSystem) {
checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
isProtectedBroadcast, registeredReceivers);
}
//構建廣播隊列
final BroadcastQueue queue = broadcastQueueForIntent(intent);
//廣播實體類
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
resultExtras, ordered, sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
if (!replaced) {
//加入到并行廣播隊列
queue.enqueueParallelBroadcastLocked(r);
//處理上面加入并行廣播消息隊列里面的廣播
queue.scheduleBroadcastsLocked();
}
//處理完之后,registeredReceivers要賦值為null
registeredReceivers = null;
NR = 0;
}
關于上面的廣播隊列BroadcastQueue,AMS內部維持了后臺廣播隊列和前臺廣播隊列
BroadcastQueue mFgBroadcastQueue;//前臺廣播隊列
BroadcastQueue mBgBroadcastQueue;//后臺廣播隊列
final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[2];
并且在AMS的構造函數中進行初始化
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", BROADCAST_FG_TIMEOUT, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
"background", BROADCAST_BG_TIMEOUT, true);
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
初始化之后,就可以像上面那樣(通過broadcastQueueForIntent方法)獲取相應的廣播隊列了,主要就是根據intent中有沒有FLAG_RECEIVER_FOREGROUND標記。
BroadcastQueue broadcastQueueForIntent(Intent intent) {
final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
if (DEBUG_BROADCAST_BACKGROUND) Slog.i(TAG_BROADCAST,
"Broadcast intent " + intent + " on "
+ (isFg ? "foreground" : "background") + " queue");
return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}
相應的,將廣播加入到隊列也很esay,調用 queue.enqueueParallelBroadcastLocked(r)
public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
mParallelBroadcasts.add(r);
r.enqueueClockTime = System.currentTimeMillis();
}
這里又要說明一下mParallelBroadcasts了,BroadcastQueue名字叫做隊列,但并不是任何集合的子類,自身不帶有存儲數據的功能,所以它的內部維護了兩個ArrayList。mParallelBroadcasts存放并行廣播(無序廣播),mOrderedBroadcasts存放串行廣播(有序廣播)
/**
* Lists of all active broadcasts that are to be executed immediately
* (without waiting for another broadcast to finish). Currently this only
* contains broadcasts to registered receivers, to avoid spinning up
* a bunch of processes to execute IntentReceiver components. Background-
* and foreground-priority broadcasts are queued separately.
*/
final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();
/**
* List of all active broadcasts that are to be executed one at a time.
* The object at the top of the list is the currently activity broadcasts;
* those after it are waiting for the top to finish. As with parallel
* broadcasts, separate background- and foreground-priority queues are
* maintained.
*/
final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();
關于最后調用scheduleBroadcastsLocked進行廣播處理的,本文不分析,下篇文章分析。
2.8 整理兩個receiver列表
上面判斷了ordered,如果ordered==false,也就是發送的是無序廣播,那么就進入并行廣播隊列直接處理掉,如果ordered==true,也就是發送的是有序廣播,需要整合將registeredReceivers里面的合并到receivers中。
// Merge into one list.
int ir = 0;
if (receivers != null) {
// A special case for PACKAGE_ADDED: do not allow the package
// being added to see this broadcast. This prevents them from
// using this as a back door to get run as soon as they are
// installed. Maybe in the future we want to have a special install
// broadcast or such for apps, but we'd like to deliberately make
// this decision.
String skipPackages[] = null;
if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())
|| Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())
|| Intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) {
Uri data = intent.getData();
if (data != null) {
String pkgName = data.getSchemeSpecificPart();
if (pkgName != null) {
skipPackages = new String[] { pkgName };
}
}
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) {
skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
}
if (skipPackages != null && (skipPackages.length > 0)) {
for (String skipPackage : skipPackages) {
if (skipPackage != null) {
int NT = receivers.size();
for (int it=0; it<NT; it++) {
// 靜態注冊的廣播是ResolveInfo類型 ,動態的是BrocastFilter
ResolveInfo curt = (ResolveInfo)receivers.get(it);
if (curt.activityInfo.packageName.equals(skipPackage)) {
receivers.remove(it);
it--;
NT--;
}
}
}
}
}
int NT = receivers != null ? receivers.size() : 0;
int it = 0;
ResolveInfo curt = null;
BroadcastFilter curr = null;
while (it < NT && ir < NR) {
if (curt == null) {
// 靜態注冊的廣播是ResolveInfo類型
curt = (ResolveInfo)receivers.get(it);
}
if (curr == null) {
// 動態注冊的廣播是BroadcastFilter類型
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;
}
}
}
/// 把優先級低于所有靜態注冊廣播接收者的動態廣播接收者都追加到receivers列表中的末尾
while (ir < NR) {
if (receivers == null) {
receivers = new ArrayList();
}
receivers.add(registeredReceivers.get(ir));
ir++;
}
2.9 處理串行廣播
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
//獲取廣播隊列
BroadcastQueue queue = broadcastQueueForIntent(intent);
//廣播實體類
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
+ ": prev had " + queue.mOrderedBroadcasts.size());
if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
"Enqueueing broadcast " + r.intent.getAction());
boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {
//加入串行廣播隊列
queue.enqueueOrderedBroadcastLocked(r);
//處理串行廣播
queue.scheduleBroadcastsLocked();
}
} else {
// There was nobody interested in the broadcast, but we still want to record
// that it happened.
if (intent.getComponent() == null && intent.getPackage() == null
&& (intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
// This was an implicit broadcast... let's record it for posterity.
addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0);
}
}
在2.6小節, 有行代碼是 final boolean replacePending = (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;這是為了查看intent的flag有沒有設置FLAG_RECEIVER_REPLACE_PENDING,如果設置的話, AMS就會在當前的系統中查看有沒有相同的intent還未處理,如果有的話,就用當前這個新的intent 來替換舊的intent。所以當replacePending==true的時候,執行queue.replaceParallelBroadcastLocked(r)進行替換,并且返回true.
public final boolean replaceParallelBroadcastLocked(BroadcastRecord r) {
for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
final Intent curIntent = mParallelBroadcasts.get(i).intent;
if (r.intent.filterEquals(curIntent)) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"***** DROPPING PARALLEL ["
+ mQueueName + "]: " + r.intent);
mParallelBroadcasts.set(i, r);
return true;
}
}
return false;
}
好了,文章比較長,在總結一下廣播的發送過程,分為九個部分。
2.1、設置Flag
2.2、檢查BroadcastOptions
2.3、當前是否有權力發出廣播
2.4、處理系統相關廣播
2.5、處理粘性廣播
2.6、registeredReceivers和receivers查詢
2.7 處理并行廣播
2.8 整理兩個receiver列表
2.9 處理串行廣播
對于粘性廣播是在注冊過程處理的,創建BroadcastRecord對象;并添加到mParallelBroadcasts隊列;
然后執行queue.scheduleBroadcastsLocked進行處理對于并行廣播: 動態注冊的廣播會進入mRegisteredReceivers表,會創建BroadcastRecord對象,并添加到mParallelBroadcasts隊列;然后執行queue.scheduleBroadcastsLocked;
對于所有靜態注冊的廣播和動態注冊的有序廣播會進入receivers表中(串行),會創建BroadcastRecord對象,并添加到mOrderedBroadcasts隊列;然后執行queue.scheduleBroadcastsLocked;
下篇文章分析廣播的處理過程,即scheduleBroadcastsLocked方法到底做了什么?暫時over!