上一篇分析了Android系統中廣播的註冊和登出,這一篇我們繼續分析傳送廣播的原始碼流程,廣播的傳送相對來說比較複雜,所以需要比較長的時間來看,如果你看懂了流程相對來說再研究流程中的細節就比較簡單了。
先看一張時序圖,因為裡面涉及到迴圈過程,程式碼中會提到,但是時序圖沒有繪製,所以需要注意一下。
0、ContextImpl.sendBroadcast
/**
* 傳送廣播
* 步驟:
* 1.廣播傳送者,即一個Activity元件或者一個Service元件,將一個特定型別的廣播傳送給AMS
* <p>
* 2.AMS接收到一個廣播後,首先找到與這個廣播對應的廣播接收者,然後將它們新增到一個廣播排程佇列中,
* 最後向AMS所執行在的先從的訊息佇列傳送一個型別為BROADCAST_INTENT_MSG的訊息,這時候對廣播傳送
* 者來說,一個廣播就傳送完了。
* <p>
* 3.當傳送到AMS所執行在的執行緒的訊息佇列中的BROADCAST_INTENT_MSG訊息被處理時,AMS就會從廣播調
* 度佇列中知道需要接受廣播的接收者,並且將對應的廣播傳送給它們所執行在的應用程式。
* <p>
* 4.廣播接收者所執行的應用程式接收到AMS傳送過來的廣播後,並不是直接將接收到的廣播傳送給廣播接收
* 者來處理,而是將接收到的廣播封裝成一個訊息,並且傳送到主執行緒的訊息佇列中。當找個訊息被處理時,
* 應用程式程式才會將它所描述的廣播發給相應的廣播接收者處理
* <p>
* 參考:
* http://blog.csdn.net/houliang120/article/details/51607170
* http://gityuan.com/2016/06/04/broadcast-receiver/
* http://blog.csdn.net/windskier/article/details/7251742
*
* @param intent The Intent to broadcast; all receivers matching this
* Intent will receive the broadcast.
*/
@Override
public void sendBroadcast(Intent intent) {
...
try {
...
// 呼叫ActivityManagerProxy中的broadcastIntent,然後通過Binder呼叫ActivityManagerService
// 中的broadcastIntent
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();
}
}
複製程式碼
之前我們講過呼叫ActivityManagerProxy的broadcastIntent方法,然後通過Binder呼叫ActivityManagerService中的對應方法。
1、ActivityManagerProxy.broadcastIntent
public int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle map,
String[] requiredPermissions, int appOp, Bundle options, boolean serialized,
boolean sticky, int userId) throws RemoteException {
...
// 通過Binder物件mRemote向AMS傳送一個型別為BROADCAST_INTENT_TRANSACTION的程式間通訊請求
mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);
...
return res;
}
複製程式碼
2、ActivityManagerService.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是否合法,如果這個時候系統正在啟動,還會驗證intent所描述的廣播是否只傳送
// 給動態註冊的廣播接收者。在系統的啟動過程中,PMS可能還未啟動,這種情況下,AMS是無法獲取靜態
// 註冊廣播接收者的,因此,就禁止傳送廣播給靜態註冊的廣播接收者
intent = verifyBroadcastLocked(intent);
// 根據caller從快取mLruProcesses中獲取程式物件ProcessRecord
final ProcessRecord callerApp = getRecordForAppLocked(caller);
...
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;
}
}
複製程式碼
3、ActivityManagerService.broadcastIntentLocked
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) {
intent = new Intent(intent);
// By default broadcasts do not go to stopped apps.
// 增加下面flag,預設不傳送廣播到已經停止的app
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
// If we have not finished booting, don`t allow this to launch new processes.
// 如果該程式還沒有完成啟動,並且不是傳送給啟動升級的廣播,則添只傳送給已註冊的廣播接收者標籤
if (!mProcessesReady && (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);// 只發給註冊的receiver
}
...
// 獲取當前傳送廣播應用所在使用者的userId
// 解釋:
// 7.1.1系統有多使用者登入,就像電腦上的主使用者和訪客模式一樣,可以設定多個使用者,每個使用者有一個id,
// 有些廣播需要多個使用者都要接收,比如時區變化這些,是同步的,每個使用者都要變化。這裡一般是我們
// 當前登入的使用者id
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
ALLOW_NON_FULL, "broadcast", callerPackage);
// Make sure that the user who is receiving this broadcast is running.
// If not, we will just skip it. Make an exception for shutdown broadcasts
// and upgrade steps.
if (userId != UserHandle.USER_ALL && !mUserController.isUserRunningLocked(userId, 0)) {
return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
}
}
...
// Verify that protected broadcasts are only being sent by system code,
// and that system code is only sending protected broadcasts.
final String action = intent.getAction();
final boolean isProtectedBroadcast;
try {
// 驗證是不是受保護的廣播(是不是系統廣播)
isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
} catch (RemoteException 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) {// 非系統程式傳送受保護廣播丟擲異常
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) {
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)) {
throw new SecurityException(msg);
}
} else {
// Limit broadcast to their own package.
// 限制傳送廣播給自己包裡
intent.setPackage(callerPackage);
}
}
}
// 下面主要是針對系統廣播的處理
if (action != null) {
switch (action) {
case Intent.ACTION_UID_REMOVED:// 移除uid
case Intent.ACTION_PACKAGE_REMOVED:// 解除安裝應用
case Intent.ACTION_PACKAGE_CHANGED:// 應用更改,比如:停用,啟動等
case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:// 外部應用不可用,比如安裝到sd卡的應用,解除安裝了sd卡
case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:// 外部應用可用
case Intent.ACTION_PACKAGES_SUSPENDED:// 暫停應用
case Intent.ACTION_PACKAGES_UNSUSPENDED:// 應用可用
switch (action) {
case Intent.ACTION_UID_REMOVED:// 移除系統userId(刪除一個使用者)
...
break;
case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:// 外部應用不可用,一般是解除安裝SD卡
...
break;
case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:// 外部應用可用,一般是插入SD卡
...
break;
case Intent.ACTION_PACKAGE_REMOVED:// 解除安裝
case Intent.ACTION_PACKAGE_CHANGED:// 更新
...
break;
case Intent.ACTION_PACKAGES_SUSPENDED:
case Intent.ACTION_PACKAGES_UNSUSPENDED:
...
break;
}
break;
case Intent.ACTION_PACKAGE_REPLACED: {// 升級應用
...
break;
}
case Intent.ACTION_PACKAGE_ADDED: {// 安裝應用
...
break;
}
case Intent.ACTION_PACKAGE_DATA_CLEARED: {// 清理應用資料
...
break;
}
case Intent.ACTION_TIMEZONE_CHANGED:// 時區改變
...
break;
case Intent.ACTION_TIME_CHANGED:// 時間改變
...
break;
case Intent.ACTION_CLEAR_DNS_CACHE:// 清理DNS快取
...
break;
case Proxy.PROXY_CHANGE_ACTION:// 代理改變
...
break;
case android.hardware.Camera.ACTION_NEW_PICTURE:// 新照片
case android.hardware.Camera.ACTION_NEW_VIDEO:// 新視訊
...
return ActivityManager.BROADCAST_SUCCESS;
}
}
// Add to the sticky list if requested.
if (sticky) {// 判斷是否是粘性廣播,如果是,AMS就需要儲存這個廣播,以便後面註冊要接收此型別廣播的接收者可以獲得這個廣播
// 檢查粘性廣播是否申請了許可權
...
if (requiredPermissions != null && requiredPermissions.length > 0) {
...
return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
}
// sticky廣播不能指定目標元件
...
// We use userId directly here, since the "all" target is maintained(維護)
// as a separate set of sticky broadcasts.
if (userId != UserHandle.USER_ALL) {// 不是傳送給所有使用者的廣播
// But first, if this is not a broadcast to all users, then
// make sure it doesn`t conflict(衝突) with an existing broadcast to
// all users.
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
UserHandle.USER_ALL);
...// 檢查是否和存在的發給所有使用者的粘性廣播一樣的廣播
}
// 在AMS中所有的粘性廣播都儲存在一個列表中,這些列表最終儲存在AMS的成員變數mStickyBroadcasts
// 所描述的一個HashMap中,並且以它們的廣播型別為關鍵字
// 首先檢查mStickyBroadcasts是否有改使用者的粘性廣播列表
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
// 該廣播列表中沒有該使用者的stick廣播列表
if (stickies == null) {
stickies = new ArrayMap<>();
mStickyBroadcasts.put(userId, stickies);
}
// 獲取註冊廣播的Action對應的粘性廣播的Intent列表
ArrayList<Intent> list = stickies.get(intent.getAction());
if (list == null) {// 如果為空
list = new ArrayList<>();// 建立一個列表
stickies.put(intent.getAction(), list);// 以action為鍵儲存該列表
}
// 獲取該action對應粘性廣播Intent列表的個數
final int stickiesCount = list.size();
int i;
// 檢查在粘性廣播列表中是否儲存了一個與引數Intent一致的廣播。如果存在直接替換,否則將引數
// Intent描述的廣播新增到粘性廣播列表list中
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) {// 如果該列表中不存在該粘性廣播的Intent加入進去
list.add(new Intent(intent));
// 我們看到粘性廣播放在了list中,而list以action為鍵放置在了stickies中,而stickies
// 又以userId為鍵放在了mStickyBroadcasts中,因此mStickyBroadcasts儲存了裝置中所有
// 使用者粘性廣播的Intent
}
}
int[] users;
if (userId == UserHandle.USER_ALL) {// 傳送廣播給全部使用者
// Caller wants broadcast to go to all started users.
// 獲取所有已啟動使用者的列表
users = mUserController.getStartedUserArrayLocked();
} else {// 傳送廣播給指定使用者
// Caller wants broadcast to go to one specific user.
users = new int[]{userId};
}
// Figure out who all will receive this broadcast.
List receivers = null;// 靜態註冊接收者
List<BroadcastFilter> registeredReceivers = null;// 動態註冊接收者
// Need to resolve the intent to interested receivers...
if ((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {// 如果當前的廣播Intent沒有指定FLAG_RECEIVER_REGISTERED_ONLY標記,也就是允許靜態註冊
// 允許靜態註冊的Intent,需要從PMS中去查詢對應的廣播接收者
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
// 如果引數intent沒有指定廣播接收者的元件名,說明是傳送給所有已註冊並且要接收該廣播的接收者的
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;
}
// registeredReceiver快結束前將註冊的BroadcastFilter放入mReceiverResolver中
// 裡面包含了對應的動態註冊的廣播接收者
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);
}
}
// 由於AMS是通過訊息機制將接收到的廣播傳送給目標廣播接收者的,因此可能會出現一種情況:上次接收
// 的廣播還沒有來得及傳送給廣播接收者,又馬上接收到一個同樣的廣播,在這種情況下,如果現在接收的
// 廣播標誌位FLAG_RECEIVER_REPLACE_PENDING等於1,那麼AMS就會用新的廣播代替舊的廣播。
final boolean replacePending =
(intent.getFlags() & Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
// 動態廣播接收者個數
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
// 處理無序廣播並且存在動態接收者,首先將當前傳送的廣播,即引數Intent所描述的物件轉發給這些動態
// 註冊的目標廣播接收者,然後轉發給靜態廣播接收者
if (!ordered && NR > 0) {
...
// 根據intent查詢對應的廣播佇列(前臺優先順序佇列還是後臺優先順序佇列),建立BroadcastRecord
final BroadcastQueue queue = broadcastQueueForIntent(intent);
// 將引數intent所描述的廣播以及動態註冊的目標廣播接收者封裝成一個BroadcastRecord物件r,
// 用來描述AMS要執行的一個廣播轉發任務
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
resultExtras, ordered, sticky, false, userId);
...
// 在BroadcastQueue中等待傳送廣播中搜尋是否有相同的BroadcastRecord並且是否替換
final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
if (!replaced) {
// 如果不需要替換則插入到BroadcastQueue中,並推動一次廣播傳送
// ------------------!!!重要!!!-------------這裡如果是非有序廣播,那就都是動態廣播接收者
// 也就是說動態廣播接收者都放在了BroadcastQueue的mParallelBroadcasts中
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}
// Merge into one list.動態廣播registeredReceivers和靜態廣播receivers合併
// 如果是order廣播,動態接收者和靜態的接收者合併到一個佇列裡面進行處理,也就是說order廣播下,
// 所有的接收者(靜態和動態)處理方式都是一樣的(後面會分析到,都是序列化處理的)。
// 還有就是對於靜態的接收者而言,始終是和order廣播的處理方式是一樣的,也就是說靜態的接收者
// 只有order模式(序列化接收)。
// 在合併過程中,如果一個動態註冊的廣播接收者和一個靜態註冊的目標廣播接收者的優先順序相同,那麼
// 動態註冊的目標接收者會排在靜態註冊的目標廣播接收者前面,即動態註冊的目標廣播接收者會優先於
// 靜態註冊的廣播接收者接受有序廣播
int ir = 0;
if (receivers != null) {// order廣播(靜態廣播)
// 對於ACTION_PACKAGE_ADDED廣播而言,如果是自己被add了,那麼這個廣播只能別人收到,
// 自己即使註冊了這個靜態廣播也接收不到,註釋上說是擔心有些應用一安裝就接收自己的PACKAGE_ADDED
// 廣播,然後就啟動了。簡言之,應用永遠接收不到自己的PACKAGE_ADDED廣播。
String skipPackages[] = null;// 需要跳過的廣播
...
// 合併中...
// 下面這一段程式碼是典型的有序連結串列的合併操作,合併的依據是接收者的priority值,這裡需要注意的一點
// 是動態廣播接收器註冊的時候一般都沒有指定priority,預設值是0,具體實現好像有點像歸併排序的意思,
// 而且是從後向前進行的歸併,因此priority越小,在連結串列中的位置就越靠前,後面處理的時候也就越先處理。
// 走完這裡所有的靜態接收者和order模式下的動態接收者都已經被合併到了receivers連結串列裡面。
// 合併以後在receivers連結串列裡面靜態接收者對應ResolveInfo物件,order模式下的動態接收者對應的
// 是BroadcastFilter物件。
int NT = receivers != null ? receivers.size() : 0;// 靜態註冊廣播接收者
int it = 0;
ResolveInfo curt = null;// 靜態接收者
BroadcastFilter curr = null;// 動態接收者
...
}
// 對於無序廣播來說,靜態註冊的廣播接收者全部儲存在列表receivers中,而對於有序廣播來說,靜態
// 註冊和動態註冊的目標廣播接收者全部儲存在receivers列表中
// 合併中...
...
// 可以看出,是在合併入receiver後統一傳送BroadcastQueue.scheduleBroadcastsLocked
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
// 將剩餘的其他目標廣播接收者封裝成另外的一個BroadcastRecord物件,用來描述AMS要執行的另
// 一個廣播轉發任務,兵器新增到AMS內部的有序廣播呼叫佇列中
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId);
// 在BroadcastQueue中等待傳送廣播中搜尋是否有相同的BroadcastRecord並且是否替換
boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {// 存在需要AMS的有序廣播呼叫佇列中增加新的額廣播任務
// ---------!!!重要!!!----------包含動態和靜態接收者
// 也就是說靜態廣播都放在BroadcastQueue的mOrderedBroadcasts中,這裡也有orderd動態廣播
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);
}
}
return ActivityManager.BROADCAST_SUCCESS;
}
複製程式碼
這裡程式碼比較多,簡單解釋一下,首先是判斷是不是系統廣播,也就是switch語句中的部分,這部分的廣播是系統發出的,根據不同廣播做出不同的處理,系統廣播我們可以接收但是不能傳送,只能由系統發出,詳細的不再解釋,自己可以看看;然後是sticky廣播的處理;然後是靜態廣播和動態廣播的處理,在動態廣播和靜態廣播處理中,先處理動態廣播接收者,再處理靜態廣播接收者,因此動態廣播接收者先收到廣播,然後是靜態廣播接收者,動態廣播和靜態廣播接收者都會通過呼叫BroadcastQueue.scheduleBroadcastsLocked方法來傳送廣播,在看這個程式碼之前先看一下時序圖中的第四步。
4、AMS.broadcastQueueForIntent
// 根據Intent所帶標籤判斷接收者是以前臺優先順序還是後臺優先順序執行,前臺接收者優先順序超時時間較短
// 正常是後臺優先順序執行,並且不會被提升到前臺優先順序
BroadcastQueue broadcastQueueForIntent(Intent intent) {
final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}
複製程式碼
這個方法主要是根據Intent中的標籤在判斷是接收者是後臺優先順序還是前臺優先順序,關於這兩個概念看上面程式碼中註釋的內容。
5、BroadcastQueue.scheduleBroadcastsLocked
// 驅動廣播,所有廣播都應該從這裡走,然後會到processNextBroadcast
public void scheduleBroadcastsLocked() {
...
// mBroadcastsScheduled用來描述AMS是否已經向它所執行在的執行緒的訊息佇列傳送了一個型別為
// BROADCAST_INTENT_MSG的訊息。AMS就是通過這個BROADCAST_INTENT_MSG訊息類排程儲存在無
// 序廣播排程佇列mParallelBroadcasts和有序廣播排程佇列mOrderedBroadcasts中的廣播轉發任務的
if (mBroadcastsScheduled) {// 如果true說明訊息佇列已經存在一個型別為BROADCAST_INTENT_MSG的訊息了
return;
}
// 雖然這裡只傳送了傳送廣播的訊息,但是這一步執行完之後就已經標記廣播傳送了,因此可以看出廣播傳送和接
// 受是非同步的,即廣播傳送者將一個廣播傳送給AMS後,不會等待AMS將這個廣播轉發給廣播接收者處理
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
複製程式碼
廣播中大多數內容的處理是在BroadcastQueue類中,上面有個mBroadcastsScheduled引數,如果為ture則阻止繼續執行,那麼我們看到下面發完訊息後會將其設定為true,我們接著看哪裡將其執行為false的,
6、BroadcastQueue.processNextBroadcast
// 廣播的核心部分,引數fromMsg是用來描述AMS類的成員函式processNextBroadcast是否是用來處理型別為
// BROADCAST_INTENT_MSG的訊息的
final void processNextBroadcast(boolean fromMsg) {
synchronized (mService) {
BroadcastRecord r;
...
if (fromMsg) {// 從msg過來的時候為true
// 前面說到,如果訊息佇列裡面有BROADCAST_INTENT_MSG訊息,該標記為true,
// 阻止新的訊息加入佇列,這裡開始處理這個訊息的時候,將mBroadcastsScheduled變數設定為false,
// 開始允許新的訊息加入。
mBroadcastsScheduled = false;
}
// 無序廣播之間不存在相互等待,這裡處理的是所有非order的動態廣播
// 處理儲存在無序廣播排程佇列mParallelBroadcasts中的廣播傳送任務,即把儲存在無序廣播排程
// 佇列mParallelBroadcasts中的廣播傳送給它的目標廣播接收者處理
while (mParallelBroadcasts.size() > 0) {
// 首先儲存無序廣播排程佇列mParallelBroadcasts中的每一個BroadcastRecord物件
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
// 呼叫deliverToRegisteredReceiverLocked向所有的receivers傳送廣播
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);
}
...
// 接下來處理儲存在有序廣播排程佇列mPendingBroadcast中的廣播轉發任務。有前面可知,有序廣播
// 排程佇列mOrderedBroadcast描述的目標廣播接收者有可能是靜態註冊的,而這些靜態註冊的目標廣
// 播接收者可能還沒有啟動起來,因此AMS將一個廣播傳送給它們處理時,首先將它們啟動起來。事實上,
// AMS只需要將他們所執行在的程式啟動起來就可以了,因為當這些程式接收到AMS傳送的廣播後,就會
// 主動將目標廣播接收者啟動起來
// mPendingBroadcast物件是用來描述一個正在等待靜態註冊的目標廣播接收者啟動起來的廣播轉發任務的
if (mPendingBroadcast != null) {
boolean isDead;
synchronized (mService.mPidsSelfLocked) {
// 檢查目標廣播接收者所在程式是否已經啟動
ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
isDead = proc == null || proc.crashing;
}
if (!isDead) {// 如果正在啟動,等待
// It`s still alive, so keep waiting
return;
} else {// 已經啟動準備傳送廣播
...
}
}
boolean looped = false;
// 逐條處理有序廣播列表mOrderedBroadcasts中的BroadcastRecord
do {
// 判斷有序廣播排程佇列mOrderedBroadcasts是否還有需要處理的廣播
if (mOrderedBroadcasts.size() == 0) {// 沒有,說明排程佇列中的廣播已經處理完成
...
return;
}
// 如果沒有處理完成,取出下一個馬上要處理的廣播BroadcastRecord
r = mOrderedBroadcasts.get(0);
boolean forceReceive = false;
// 獲取廣播轉發任務的目標廣播接收者的個數
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
// 檢查前一個目標廣播接收者是否在規定的時間內處理完成AMS給它傳送的一個有序廣播。AMS處理
// BroadcastRecord物件r所描述的一個廣播轉發任務時,會將當前時間記錄在這個BroadcastRecord
// 物件中,如果這個廣播任務不能在(2*BROADCAST_TIMEOUT*numReceivers)毫秒內完成,即它的目
// 標廣播接收者不能在(2*BROADCAST_TIMEOUT*numReceivers)毫秒內完成AMS給它們傳送的一個有
// 序廣播,那麼會呼叫broadcastTimeoutLocked函式來強制結束這個廣播轉發任務,
if (mService.mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2 * mTimeoutPeriod * numReceivers))) {
...
// 出現超時,強制結束
broadcastTimeoutLocked(false); // forcibly finish this broadcast
// 重置引數,繼續處理有序廣播排程佇列mOrderedBroadcasts的下一個廣播轉發任務
forceReceive = true;
r.state = BroadcastRecord.IDLE;
}
}
// 檢測廣播轉發任務是否正在處理中,即AMS正在將一個有序廣播轉發給它的前一個目標廣播接收處理者,
// 如果是,AMS就會等待這個目標廣播接收者處理完該有序廣播,然後再轉發給下一個廣播接收者處理
...
// 表示廣播已經向所有的receiver傳送結束或者中途被取消, 如果r.resultAbort為true,會停止處理
// 當前正在傳送的BroadcastRecord,這樣優先順序比較低的接收者也就收不到這個廣播了
// 檢查BroadcastRecord物件r所描述的廣播轉發任務是否已經處理完成,或者是否已經被強制結束了。
// 如果是,那麼呼叫函式cancelBroadcastTimeoutLocked來刪除前面傳送到AMS所執行在的執行緒的訊息
// 佇列中的一個BROADCAST_TIMEOUT_MSG訊息,表示BroadcastRecord物件r所描述的廣播轉發任務已經
// 在規定時間內處理完成了。接下來就講改廣播BroadcastRecord物件從佇列中刪除,然後賦值為null
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);
// Set this to null so that the reference
// (local and remote) isn`t kept in the mBroadcastHistory.
r.resultTo = null;
} catch (RemoteException e) {
...
}
}
...
// ... and on to the next...
...
// BroadcastRecord處理完移除
mOrderedBroadcasts.remove(0);
r = null;
looped = true;
continue;
}
} while (r == null);// 如果第一次取出的r不為空,則退出迴圈
// Get the next receiver...(獲取下一個將要處理的廣播接收者在其列表中的位置)
int recIdx = r.nextReceiver++;
// Keep track of when this receiver started, and make sure there
// is a timeout message pending to kill it if need be.
// 儲存當前時間,
r.receiverTime = SystemClock.uptimeMillis();
if (recIdx == 0) {// 表示第一個開始處理的接收者,也就是BroadcastRecord物件r所描述的廣播任務剛被處理
// 接收者開始處理的時間戳,也就是這個接收者開始處理了,要記錄開始時間來計算是否超過超時時間
// 也就是說這是BroadcastRecord中第一個接收者開始被處理的時間戳,也就是上面BroadcastRecord
// 超時的起點,可以看到上面超時比較的時候用的就是r.dispatchTime
r.dispatchTime = r.receiverTime;
r.dispatchClockTime = System.currentTimeMillis();
...
}
// 檢查AMS是否已經向它所在的執行緒的訊息佇列傳送了型別為BROADCAST_TIMEOUT_MSG的訊息,如果沒有傳送,
// 那麼會呼叫setBroadcastTimeoutLocked函式向這個執行緒傳送一個型別為setBroadcastTimeoutLocked
// 的訊息,並且制定在timeoutTime毫秒後處理。上面指定了r.receiverTime為當前時間表示AMS將一個有序
// 廣播傳送給BroadcastRecord物件r所描述的廣播轉發任務的下一個目標廣播接收者處理的時間。如果這個
// 廣播接收者不能再timeoutTime之內完成這個有序廣播,AMS就會任務它超時。
if (!mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mTimeoutPeriod;
...
// 設定超時,傳入引數是r.receiverTime + mTimeoutPeriod,也就是開始時間加上超時時間
// mTimeoutPeriod,mTimeoutPeriod初始化是在BroadcastQueue初始化的時候傳入的,
// 也就是在AMS(AMS建構函式中)中初始化mFgBroadcastQueue和mBgBroadcastQueue時傳入的
// BROADCAST_FG_TIMEOUT = 10 * 1000和BROADCAST_BG_TIMEOUT = 60 * 1000,
// 這裡開始埋了ANR的雷
setBroadcastTimeoutLocked(timeoutTime);
}
// 上面分析的時候對於BroadcastRecord.receivers裡面包含兩種receiver接收者:order廣播下的
// 動態註冊接收者和靜態接收者,這兩種receiver處理的方式是不一樣的,對於order廣播下的動態註冊
// receiver而言,接收者程式一定是已經啟動的,但是對於靜態接收者receiver而言,當前的receiver程式
// 可能還沒有啟動,因此動態和靜態的receiver處理的邏輯不一樣,需要分開處理,而靜態接收者又分為程式
// 已經啟動和尚未啟動兩種情況。
final BroadcastOptions brOptions = r.options;
// 得到下一個廣播接收者
final Object nextReceiver = r.receivers.get(recIdx);
// 如果當前nextReceiver是一個BroadcastFilter型別,說名是一個動態註冊接收者,不需要啟動一個程式,
// 直接呼叫deliverToRegisteredReceiverLocked函式傳送廣播
if (nextReceiver instanceof BroadcastFilter) {
// Simple case: this is a registered receiver who gets
// a direct call.
BroadcastFilter filter = (BroadcastFilter) nextReceiver;
...
// 上面已經分析
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
// 檢查BroadcastRecord物件r所描述的廣播轉發任務是否用來轉發無序廣播的。
if (r.receiver == null || !r.ordered) {// 如果是
...
// 設定IDLE狀態,表示AMS不需要等待它的前一個目標廣播接收者處理完成一個廣播就可以將該廣播
// 繼續傳送給它的下一個目標廣播接收者處理
r.state = BroadcastRecord.IDLE;
// 呼叫下面函式就是為了將一個廣播繼續傳送給BroadcastRecord物件r所描述的廣播轉發任務的
// 下一個目標廣播接收者處理的
scheduleBroadcastsLocked();
} else {
...
}
return;
}
// Hard case: need to instantiate the receiver, possibly
// starting its application process to host it.
// 如果不是動態的說明是一個靜態註冊接收者(如果動態的上面if中處理並進行攔截),此時程式可能沒有啟動
ResolveInfo info =
(ResolveInfo) nextReceiver;
ComponentName component = new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
// 是否跳過該廣播接收者不處理
boolean skip = false;
...
// 得到ResolveInfo物件info所描述的廣播接收者的android:process屬性值,即它需要執行在的應用程式
// 程式的名稱,並且儲存在變數targetProcess中
String targetProcess = info.activityInfo.processName;
// 獲取當前廣播接收者的程式記錄,也就是該靜態廣播接收者是否已經執行
ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
info.activityInfo.applicationInfo.uid, false);
...
// 跳過,恢復初始狀態,開始下一個廣播接收者的處理
if (skip) {
...
return;
}
r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
r.state = BroadcastRecord.APP_RECEIVE;
r.curComponent = component;
r.curReceiver = info.activityInfo;
...
if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
scheduleTempWhitelistLocked(receiverUid,
brOptions.getTemporaryAppWhitelistDuration(), r);
}
// Broadcast is being executed, its package can`t be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
...
}
// Is this receiver`s application already running?
// 如果當前程式已經執行,則直接發給該程式,然後返回
if (app != null && app.thread != null) {
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
// 將廣播傳送給該程式處理
processCurBroadcastLocked(r, app);
// order廣播是一種同步處理方式,因此處理完可以直接return
return;
} catch (RemoteException e) {
...
} catch (RuntimeException e) {
...
return;
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
...
// 靜態接收者程式尚未啟動,呼叫AMS的startProcessLocked函式啟動該接收者程式,並將當前正在等待程式
// 啟動的BroadcastRecord儲存到mPendingBroadcast裡面,這個就是靜態廣播拉起應用的原理,如果應用
// 沒有啟動,註冊一個靜態廣播(一般廠商會修改只能啟動自己的應用)。到這裡又開始進入等待程式啟動的
// 過程,程式啟動完成後才能處理廣播
if ((r.curApp = mService.startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
"broadcast", r.curComponent,
(r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
== null) {// 如果啟動失敗
...
logBroadcastReceiverDiscardLocked(r);
// 結束廣播傳送任務
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
r.state = BroadcastRecord.IDLE;
return;
}
// 正在啟動接收者程式,將正在啟動的BroadcastRecord記錄儲存到mPendingBroadcast中,同時將當前正在
// 啟動的接收者程式在所有接收者中的索引儲存到mPendingBroadcastRecvIndex,如果當前廣播接收者處理
// 完,需要繼續從mPendingBroadcastRecvIndex計算到下一個接收者傳送當前廣播
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
}
}
複製程式碼
我們根據第五步的傳送訊息型別可以跟蹤到,訊息執行是在BroadcastHandler的handleMessage方法中呼叫的,這裡出入一個引數fromMsg,也就是該廣播是不是通過Handler傳送訊息的方式傳遞的,那麼我們上面知道我們這就是通過這種方式執行的,所以在程式碼中會將mBroadcastsScheduled引數設定為false,也就是這個訊息開始執行了則開始執行下一個訊息傳送。還有就是通過直接呼叫的方式來執行的,那麼就不會改變這個引數。
接著看,先執行無序廣播,無序廣播接收者不需要等待,所以直接for迴圈執行,呼叫deliverToRegisteredReceiverLocked方法傳送廣播。
7、deliverToRegisteredReceiverLocked
// AMS將一個廣播發給一個目標廣播接收者之前,有可能需要檢查這個廣播的傳送者和接收者的許可權。這個許可權檢查是
// 雙向的,即需要檢查一個廣播傳送者是否有許可權向一個目標廣播接收者傳送廣播,以及一個目標廣播接收者是否有許可權
// 接收一個廣播傳送者發過來的一個廣播。這兩個許可權主要是呼叫AMS的成員函式checkComponentPermission來檢查
// 對方的PID以及UID是否符合要求
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered, int index) {
// 標記是否要跳過該廣播接收者
boolean skip = false;
// 需要檢查廣播傳送者的許可權
...
// r.requiredPermissions != null為true表示需要檢查廣播接收者的許可權
...
// 如果要跳過,則設定該廣播結束
if (skip) {
r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
return;
}
...
// order廣播,所有的接收者需要依次以一種同步的方式傳送廣播,
// 可以看到order廣播在BroadcastRecord儲存了幾個狀態
if (ordered) {
// IBinder型別,代表當前的接收者
r.receiver = filter.receiverList.receiver.asBinder();
// 當前正在處理的BroadcastFilter,和上面的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.curReceiver = r;
mService.updateOomAdjLocked(r.curApp);
}
}
try {
if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
// 如果正在備份或者恢復備份跳過,
// 如果是一個有序廣播,則執行下一個廣播
if (ordered) {
skipReceiverLocked(r);
}
} else {
// 如果不需要進行許可權檢查或者通過許可權檢查,呼叫performReceiveLocked傳送廣播
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
}
...
} catch (RemoteException e) {
...
}
}
複製程式碼
我們這裡不是有序廣播,正常也不會備份,所以會走else裡面的方法performReceiveLocked。如果是有序廣播我們後面再分析
8、performReceiveLocked
/**
* 由前面可知,當一個Activity或者一個Service將一個廣播接收者註冊到AMS中時,它所在的應用程式程式會先將
* 這個廣播接收者封裝成一個型別為InnerReceiver的Binder本地物件,然後再註冊到AMS中。因此當AMS要將一個
* 廣播發給一個目標廣播接收者處理時,實際上是將這個廣播轉發給封裝了這個目標廣播接收者的一個InnerReceiver
* 物件來處理
* <p>
* AMS向一個應用程式程式傳送一個廣播時,採用的是非同步程式間通訊方式。前面提到,傳送給一個Binder實體物件
* 的所有非同步事物都是儲存在一個非同步事物佇列中的。由於儲存在一個非同步事物佇列中的非同步事物在同一時刻只有一
* 個會得到處理,即只有佇列頭部的非同步事物才會得到處理,因此AMS就可以保證它傳送給同一個應用程式的所有廣
* 播都是按照這個傳送順序來序列的被接受和處理
*/
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
// app不為空,表示程式已經啟動,呼叫ActivityThread.scheduleRegisteredReceiver傳送當前廣播
if (app != null) {
if (app.thread != null) {// 因為是動態註冊廣播,所以一般不為空
try {
// 這裡scheduleRegisteredReceiver函式是一個Binder呼叫,註釋上面說的很清楚,
// 要用one-way calls像動態的註冊程式發起Binder呼叫,意思就是在Binder呼叫裡面會
// 加上IBinder.FLAG_ONEWAY標記,Binder客戶端(動態註冊程式)只要一收到Binder呼叫的
// 命令和資料,立馬返回到Binder服務端(AMS程式),是一個非同步的呼叫方式。
// 大致流程是:
// 1) ApplicationThreadNative.ApplicationThreadProxy. scheduleRegisteredReceiver(system_server)
// 2) Binder驅動 (Binder驅動程式,ONEWAY)
// 3)ApplicationThreadNative. scheduleRegisteredReceiver(應用程式,Binder執行緒向主執行緒傳送訊息)
// 4)Binder驅動返回 (Binder驅動程式)
// 5)ActivityThread. scheduleRegisteredReceiver(應用程式,處理訊息)
// 呼叫ApplicationThread物件的Binder代理物件的函式來向它傳送廣播
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.repProcState);
...
} catch (RemoteException ex) {
...
}
} else {
...
}
} else {
// 直接呼叫與它關聯的一個InnerReceiver物件的Binder代理物件的成員函式performReceive來向它傳送廣播
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
}
複製程式碼
如果程式存在,則執行ActivityThread.scheduleRegisteredReceiver方法,否則直接呼叫receiver.performReceive方法傳送廣播。因為我們動態註冊的所以會執行第一種情況。
9、ActivityThread.scheduleRegisteredReceiver
// 處理非序列化動態廣播,非串化ordered是false,這裡的receiver對應的是
// LoadedApk.ReceiverDispatcher.InnerReceiver物件
// This function exists to make sure all receiver dispatching is
// correctly ordered, since these are one-way calls and the binder driver
// applies transaction ordering per object for such calls.
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras, boolean ordered,
boolean sticky, int sendingUser, int processState) throws RemoteException {
updateProcessState(processState, false);
// 呼叫LoadedApk.ReceiverDispatcher.InnerReceiver.performReceive函式
receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
sticky, sendingUser);
}
複製程式碼
這裡我們看到和上面一樣最終還是執行receiver.performReceive方法來傳送廣播。
10、LoadedApk.ReceiverDispatcher.InnerReceiver.performReceive
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
final LoadedApk.ReceiverDispatcher rd;
...
rd = mDispatcher.get();
...
if (rd != null) {
rd.performReceive(intent, resultCode, data, extras,
ordered, sticky, sendingUser);
} else {
...
try {
...
mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
} catch (RemoteException e) {
...
}
}
}
}
複製程式碼
我們上一章註冊廣播是講過LoadedApk.ReceiverDispatcher物件封裝了廣播接收者,如果該廣播接收者註冊了,那麼該物件就會存在,則會呼叫LodedApk.ReceiverDispatcher.performReceive,否則呼叫AMS.finishReceiver方法,我們先看有廣播的情況。
11、LodedApk.ReceiverDispatcher.performReceive
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
// 首先將引數Intent所描述的一個廣播封裝成一個Args物件,然後將這個Args物件封裝成一個訊息物件,
// 然後將這個訊息物件傳送到應用程式主執行緒的訊息佇列中。
final Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
// 將當前廣播資訊放到主執行緒的Handler中進行處理,作為一個Runnable排程而不是在handleMessage中處理,
// 而是在Handler內部機制中,處理的時候會對應run函式,因此這裡不久後會呼叫Args.run函式
..
// 上面將廣播的引數封裝在一個Args物件裡面,然後通過post到主執行緒的訊息佇列裡面
if (intent == null || !mActivityThread.post(args)) {
if (mRegistered && ordered) {
...
args.sendFinished(mgr);
}
}
}
複製程式碼
上面初始化了一個Args物件,該物件實現了Runnable介面,在if語句中呼叫post方法,會呼叫Args中的run方法。
13、Args.run
public void run() {
// mReceiver指向一個廣播接收者
final BroadcastReceiver receiver = mReceiver;
...
// 這裡處理的是動態廣播接收者,預設認為接收者BroadcastReceiver已經存在
try {
...
// 接受廣播
receiver.onReceive(mContext, intent);
// 然後呼叫BroadcastReceiver.PendingResult.finish函式,也就是下面的finish函式
} catch (Exception e) {
// 檢查當前廣播是否是有序廣播,並且廣播接收者是否已經註冊到AMS中
if (mRegistered && ordered) {
// 通知AMS,它前面轉發過來的有序廣播已經處理完了,這時AMS就可以繼續將這個有序廣播
// 轉發給下一個目標廣播接收者了
sendFinished(mgr);
}
...
}
...
}
複製程式碼
在這裡呼叫BroadcastReceiver.onReceive方法,這樣就會執行完一次無需廣播傳送過程。我們再回到第10步,如果物件接收者不存在則呼叫AMS.finishReceiver
15、AMS.finishReceiver
public void finishReceiver(IBinder who, int resultCode, String resultData,
Bundle resultExtras, boolean resultAbort, int flags) {
...
try {
boolean doNext = false;
BroadcastRecord r;
// 首先辨別出當前receiver所在的BroadcastRecord屬於前臺廣播還是後臺廣播,然後在對應的
// BroadcastQueue中找出對應的BroadcastRecord,裡面的finishReceiverLocked函式在前面介紹過,
// 主要是重新設定BroadcastRecord裡面一些狀態變數,以便於BroadcastRecord將廣播傳送給下一個
// 接收者。尤其的,如果前面的mAbortBroadcast設定為true,那麼BroadcastRecord的成員resultAbort
// 會設定成true
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);
}
}
// 立馬排程一次傳送廣播,傳送下一次廣播,但是processNextBroadcast是一個同步函式,
// 一次只能處理一個請求
if (doNext) {
r.queue.processNextBroadcast(false);
}
...
} finally {
...
}
}
複製程式碼
這裡是如果傳送廣播時接收者不存在,那麼要完成該次廣播,並且判斷是否執行傳送給下一個廣播接收者,如果需要傳送給下個廣播接收者要再次呼叫BroadcastQueue.processNextBroadcast方法。這樣就又回到了前面第六步。我們再往前看BroadcastQueue.processNextBroadcast方法,執行完無序廣播後開始執行有序廣播,因為有序廣播是一個執行完再執行下一個所以必須設定超時,並且如果超時要立即接受廣播,
17、broadcastTimeoutLocked
/**
* AMS在處理order模式廣播接收者時,會為每一個order模式廣播處理設定超時時間,並且超時時間是各個接收者之間
* 相互獨立,前面分析超時通過setBroadcastTimeoutLocked函式建立超時時間點訊息的,本以為每次處理完以後,
* 呼叫cancelBroadcastTimeoutLocked函式取消當前接收者的超時訊息,但是實際上用了一種更加高效的方法處理了
* 超時機制,在每個order模式receiver開始處理的時候設定超時訊息,BroadcastRecord.receiverTime記錄了
* 當前receiver開始處理的時間點
* <p>
* BroadcastRecord超時:注意這裡的超時和我們常說的廣播超時ANR不是一個概念,這個BroadcastRecord超時是針
* 對當前BroadcastRecord. receivers裡面剩餘的所有的成員而言的,比如說當前receivers裡面剩餘4個廣播接收者,
* 那麼這個超時的時間: 2*(4*mTimeoutPeriod) 至於這個mTimeoutPeriod,對於前臺廣播mFgBroadcastQueue
* 和mBgBroadcastQueue後臺廣播時間:BROADCAST_FG_TIMEOUT = 10 * 1000
* 和BROADCAST_BG_TIMEOUT = 60 * 1000(AMS中),也就是對於有4個成員的receivers 後臺廣播的
* BroadcastRecord而言超時的時間為:2*(4*10*1000)=80 000ms=80s,當出現這種超時的時候,當前正在處理的
* 廣播接收者會出現ANR,並且導致後面尚未接收到廣播的收不到當前的廣播。broadcastTimeoutLocked函式會將
* mOrderedBroadcasts中下標為0的應用程式ANR,下面forceReceive設定為true,走到下面的if判斷裡面會將當前
* 正在處理的BroadcastRecord從mOrderedBroadcasts中remove掉,導致receivers後面的成員沒有收到廣播,
* 並且將r設定為null,接著就處理broadcastTimeoutLocked裡面的下一個廣播記錄BroadcastRecord。例如,
* receivers裡面包含4個成員,但是第1個接收者在80s內都沒有處理完,那麼這個接收者程式會收到ANR,
* 並且後面的3個廣播接收者都收不到當前的廣播。
* <p>
* 註釋來自於:http://blog.csdn.net/houliang120/article/details/51607170
*
* @param fromMsg
*/
final void broadcastTimeoutLocked(boolean fromMsg) {
...
long now = SystemClock.uptimeMillis();
BroadcastRecord r = mOrderedBroadcasts.get(0);
if (fromMsg) {
if (mService.mDidDexOpt) {
...
long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
setBroadcastTimeoutLocked(timeoutTime);
return;
}
...
long timeoutTime = r.receiverTime + mTimeoutPeriod;
// 舉個例子:
// 假設receiverA在100s的時候開始處理,超時時間為10s,那麼receiverA的超時時間點就是110s,
// 但是receiverA在105s的時候已經處理完了,於是在105s的時候開始receiverB,但是並沒有取消
// receiverA的超時訊息,也就是在110s的時候仍然會走到這裡的broadcastTimeoutLocked函式,
// receiverB開始處理,這時候r.receiverTime就是105s,對於receiverB而言超時時間應該是115s,
// 假設receiverB需要在112s才能處理完,在110s的時候broadcastTimeoutLocked函式處理的時候
// timeoutTime=115s,now=110s,這時候不會進行實際的超時處理,因為還沒有到真實的超時時間,
// 所以重新設定超時時間點在115s。就這樣根據當前BroadcastRecord.receiverTime的時間反覆調整。
if (timeoutTime > now) {// 判斷超時時間點和現在時間的關係,此處還沒有超時
// We can observe premature timeouts because we do not cancel and reset the
// broadcast timeout message after each receiver finishes. Instead, we set up
// an initial timeout then kick it down the road a little further as needed
// when it expires.
// 因為沒有在每個廣播處理完之後取消或者重置超時時間,從而導致提前檢測到超時訊息。取而代之,
// 設定一個初始超時時間點,然後每次出現超時事件的時候根據需要進行處理或者調整超時機制。
...
setBroadcastTimeoutLocked(timeoutTime);
return;
}
}
BroadcastRecord br = mOrderedBroadcasts.get(0);
if (br.state == BroadcastRecord.WAITING_SERVICES) {
...
processNextBroadcast(false);
return;
}
...
}
複製程式碼
這裡主要是超時的處理,因為註釋已經非常詳細,所以不再解釋,所以我們在寫廣播接收到訊息是最後採用Handler或者啟動服務將訊息處理的過程放到Handler中或者服務中,而不是直接在廣播接收者中完成。
18、setBroadcastTimeoutLocked
// 設定超時時間
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (!mPendingBroadcastTimeoutMessage) {
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
}
}
複製程式碼
如果廣播還沒有超時就執行完了就從新設定下一個廣播的起始時間,方便計算下一個廣播的超時。這個訊息處理就不分析了,自己看一下。再回到第六步BroadcastQueue.processNextBroadcast方法中,如果廣播傳送完成,或者被中斷或者取消,則判斷是否傳送最後的廣播,如果要傳送,則執行performReceiveLocked方法,這個方法在第八步講了,這裡就不再重複分析。我們知道廣播有靜態廣播接收者和動態廣播接收者,動態廣播接收者程式都是啟動的,但是靜態的就不一定了,可能程式還沒有啟動,就想第六步中程式碼註釋分析的一樣,我們上面處理的都是動態註冊的,不需要判斷程式是否已經啟動,但是靜態廣播就需要判斷該程式是否已經啟動了。這裡的ResolveInfo表示靜態廣播接收者物件。找到物件後判斷是否要跳過該廣播,如果跳過則通過執行scheduleBroadcastsLocked方法傳送訊息執行下一個廣播。如果不需要跳過該廣播時,判斷該程式是否存在,如果該靜態廣播的程式已經存在了則執行processCurBroadcastLocked,將廣播傳送給該程式處理,如果不存在則啟動程式,並且將廣播物件BroadcastRecord放入等待廣播列表中,如果啟動失敗則接受該廣播並判斷是否執行下一個廣播,我們先將一下程式存在時廣播的處理,最後再分析程式啟動時的廣播處理。
21、processCurBroadcastLocked
// 這裡只是將廣播傳送到接收者程式,需要一直等待接收者程式處理完廣播後返回,AMS才能處理當前BroadcastRecord
// 裡面的下一個receiver,所以直接返回就行了,反正需要等待的。
private final void processCurBroadcastLocked(BroadcastRecord r,
ProcessRecord app) throws RemoteException {
...
// 將程式的相關資訊寫入當前BroadcastRecord中相關的接收者
...
boolean started = false;
try {
...
// 處理廣播,等待接收程式的返回
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
app.repProcState);
...
} finally {
...
}
}
複製程式碼
呼叫scheduleReceiver方法處理廣播。
22、ApplicationThread.scheduleReceiver
// 處理應用程式中接收到的靜態廣播訊息,實際處理該廣播的是ActivityThread.handleReceiver函式
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
boolean sync, int sendingUser, int processState) {
updateProcessState(processState, false);
ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
sync, false, mAppThread.asBinder(), sendingUser);
..
sendMessage(H.RECEIVER, r);
}
複製程式碼
通過Handler訊息處理機制呼叫handleReceiver方法處理該靜態廣播
23、ApplicationThread.handleReceiver
// 主要包括三步:
// 1) 建立BroadcastReceiver物件
// 2) 執行onReceive函式
// 3) 向AMS傳送處理結束訊息
private void handleReceiver(ReceiverData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
// 1) 建立BroadcastReceiver物件
// 這裡處理的是靜態廣播接收者,預設認為接收者BroadcastReceiver物件不存在
// 每次接受都會建立一個新的BroadcastReceiver物件
String component = data.intent.getComponent().getClassName();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
IActivityManager mgr = ActivityManagerNative.getDefault();
BroadcastReceiver receiver;
try {
// 首先從AMS傳遞的intent中獲取當前處理該廣播的元件名稱,然後通過反射建立一個BroadcastReceiver
// 物件,從這裡可以看出來,靜態廣播處理的時候,每次都會建立一個新的BroadcastReceiver物件;
...
receiver = (BroadcastReceiver) cl.loadClass(component).newInstance();
} catch (Exception e) {
...
}
// 2) 執行onReceive函式
try {
// 建立Application物件,如果程式已經啟動,Application物件已經建立
Application app = packageInfo.makeApplication(false, mInstrumentation);
...
// 呼叫接收者的onReceive方法,這裡還呼叫了setPendingResult方法,詳細內容請看
// BroadcastReceiver.goAsync方法。
receiver.setPendingResult(data);
receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
} catch (Exception e) {
...
} finally {
sCurrentBroadcastIntent.set(null);
}
// 3) 向AMS傳送處理結束訊息
if (receiver.getPendingResult() != null) {
data.finish();
}
}
複製程式碼
上面最開始註釋寫了這裡分為三步,建立BroadcastReceiver物件,執行onReceive函式,結束。這樣程式存在時靜態廣播就傳送完成了。最後我們分析需要啟動程式時傳送廣播的流程,我們先看一張時序圖。
在Android系統原始碼分析–Process啟動過程一章我們分析了程式啟動過程,在最後會呼叫ActivityThread.mian方法,我們從這個方法開始看:
0、ActivityThread.mian
/**
* 啟動新的程式時呼叫Process的start方法會最終呼叫改函式
* 啟動新的程式主要做了兩件事:
* 1.在程式中建立了一個ActivityThread物件,並呼叫了它的成員函式attach向AMS傳送一個啟動完成的通知
* 2.呼叫Looper類的靜態成員函式prepareMainLooper建立一個訊息迴圈,並且在向AMS傳送啟動完成通知後,
* 使得當前程式進入到這個訊息迴圈中
*
* @param args
*/
public static void main(String[] args) {
...
ActivityThread thread = new ActivityThread();
thread.attach(false);
...
}
複製程式碼
這裡會初始化一個ActivityThread物件,然後呼叫該物件的attach方法,傳入引數false。
1、ActivityThread.attach
private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
...
// 獲取AMS的代理物件,下面會呼叫它的成員函式attachApplication向AMS傳送一個程式間通訊請求,並且
// 將前面所建立的ApplicationThread(mAppThread)物件傳遞給AMS
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
// 這裡向ActivityManagerService註冊Client端的binder物件,它是一個binder執行緒
// ActivityManagerService中有關Activity宣告週期的訊息都會傳送到ActivityThread中的
// 主執行緒mH處理
// mAppThread是一個Binder本地物件,AMS是通過它來和應用程式通訊的。(AMS的代理物件型別為
// ActivityManagerProxy,因此,接下來會呼叫ActivityManagerProxy類的成員函式向AMS傳送一個
// 程式間通訊請求)
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
} else {// system thread
...
}
...
}
複製程式碼
我們上面接受了傳入引數是false,則會走if中的程式碼,也就是會執行mgr.attachApplication方法,通過註釋我們知道會呼叫ActivityManagerProxy.attachApplication方法,然後傳遞到ActivityManagerService.attachApplication方法。
2、ActivityManagerProxy.attachApplication
public void attachApplication(IApplicationThread app) throws RemoteException {
...
// 通過mRemote(Binder)向AMS傳送一個型別為ATTACH_APPLICATION_TRANSACTION的程式間通訊請求
mRemote.transact(ATTACH_APPLICATION_TRANSACTION, data, reply, 0);
...
}
複製程式碼
3、ActivityManagerService.attachApplication
// 用來處理ActivityMangerProxy傳遞過來的型別為ATTACH_APPLICATION_TRANSACTION的程式間通訊請求
@Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
...
attachApplicationLocked(thread, callingPid);
...
}
}
複製程式碼
這裡呼叫attachApplicationLocked方法:
4、attachApplicationLocked
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
...
// Check if a next-broadcast receiver is in this process...
if (!badApp && isPendingBroadcastProcessLocked(pid)) {
try {
didSomething |= sendPendingBroadcastsLocked(app);
} catch (Exception e) {
// If the app died trying to launch the receiver we declare it `bad`
Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
badApp = true;
}
}
...
}
複製程式碼
因為我們這裡分析傳送廣播,所以我們只關心廣播的相關處理,所以上面只保留了廣播的內容,if語句中判斷當前應用沒有問題,並且有等待的廣播,才會呼叫sendPendingBroadcastsLocked方法,前面我們知道靜態廣播有一種是程式不存在的,所以這個廣播就要放到等待廣播中,這裡就開始處理等待廣播的情況。
5、sendPendingBroadcastsLocked
// The app just attached; send any pending broadcasts that it should receive
boolean sendPendingBroadcastsLocked(ProcessRecord app) {
boolean didSomething = false;
for (BroadcastQueue queue : mBroadcastQueues) {
didSomething |= queue.sendPendingBroadcastsLocked(app);
}
return didSomething;
}
複製程式碼
mBroadcastQueues是包含前臺優先順序和後臺優先順序的廣播佇列,這裡分別呼叫前臺和後臺優先順序廣播的BroadcastQueue.sendPendingBroadcastsLocked方法。
6、BroadcastQueue.sendPendingBroadcastsLocked
// 未啟動程式的廣播接收者需要先啟動程式,最後到達這個函式
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
boolean didSomething = false;
// 前面分析mPendingBroadcast用於儲存當前正在等待程式啟動的BroadcastRecord
final BroadcastRecord br = mPendingBroadcast;
if (br != null && br.curApp.pid == app.pid) {
...
try {
// 啟動完成設定為null
mPendingBroadcast = null;
processCurBroadcastLocked(br, app);
didSomething = true;
} catch (Exception e) {
...
}
}
return didSomething;
}
複製程式碼
這裡是找到等待處理的廣播並且判斷是否為空,並且是否和當前程式的pid相同,也就是是不是找個程式的等待廣播,如果是就呼叫processCurBroadcastLocked方法進行處理,這個方法在上面第21步中已經講過,所以又回到了程式存在的情況下廣播的處理。這樣整個廣播的處理就分析完了,程式碼量很大,但是邏輯很清楚,只需要對著註釋多看看看就明白了。下一篇我們開始講Activity啟動的原始碼分析。
為了方便記憶,這裡新增一個傳送廣播的流程圖,這樣對照著上面的程式碼流程再看就會好理解很多。
注
Android開發群:192508518
微信公眾賬號:Code-MX
首發地址:www.codemx.cn
注:本文原創,轉載請註明出處,多謝。