說說Android的廣播(3)
什麼樣的廣播是併發的?
現在讓我們開始破解Android中的一個trick,普通廣播都是併發的嗎?
帶著這個問題,我們來看ActivityManagerService.broadcastIntentLocked中的實現邏輯。
broadcastIntentLocked中的細節很多,我們放到後面講,我們先只把跟併發和序列佇列有關的部分專門提煉出來。
receivers和registeredReceivers
畫重點了,畫重點了啊!下面我們要學習兩個重要的概念,一個是receivers,另一個是registeredReceivers。這兩個東西的區別真正決定了它是能併發接收到廣播還是序列地接收廣播。
通俗地講,registeredReceivers就是通過Java程式碼在執行時註冊的receiver,而receivers是連通過AndroidManifest.xml中註冊的也算上。
如果想只發給動態註冊的BroadcastReceiver,可以設定有個FLAG_RECEIVER_REGISTERED_ONLY屬性。
在處理普通廣播處理的流程時,就先定義了兩個List,一個是receivers,另一個是registeredReceivers.
16820 // Figure out who all will receive this broadcast.
16821 List receivers = null;
16822 List<BroadcastFilter> registeredReceivers = null;
我們分別看看,它們都是在哪裡被賦值的:
如剛才所說,只有在不設FLAG_RECEIVER_REGISTERED_ONLY的情況下,才需要查完整的receiver列表。
16823 // Need to resolve the intent to interested receivers...
16824 if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
16825 == 0) {
16826 receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
16827 }
如果intent.getComponent() == null,則通過queryIntent方法來查詢registeredReceivers。
16828 if (intent.getComponent() == null) {
...
16847 registeredReceivers = mReceiverResolver.queryIntent(intent,
16848 resolvedType, false, userId);
...
16850 }
好,兩個receiver相關的列表都被賦值好了。下面揭曉奇蹟的時候來了,究竟是哪個被翻牌子了呢?
16858 int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
16859 if (!ordered && NR > 0) {
16860 // If we are not serializing this broadcast, then send the
16861 // registered receivers separately so they don't wait for the
16862 // components to be launched.
16863 final BroadcastQueue queue = broadcastQueueForIntent(intent);
16864 BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
16865 callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
16866 appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
16867 resultExtras, ordered, sticky, false, userId);
16868 if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
16869 final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
16870 if (!replaced) {
16871 queue.enqueueParallelBroadcastLocked(r);
16872 queue.scheduleBroadcastsLocked();
16873 }
16874 registeredReceivers = null;
16875 NR = 0;
16876 }
沒錯,是非ordered的registered receivers被併發執行了。
那麼receivers是如何被處理的呢?一句話,被當成ordered處理了.
我們看程式碼:
16950 if ((receivers != null && receivers.size() > 0)
16951 || resultTo != null) {
16952 BroadcastQueue queue = broadcastQueueForIntent(intent);
16953 BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
16954 callerPackage, callingPid, callingUid, resolvedType,
16955 requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
16956 resultData, resultExtras, ordered, sticky, false, userId);
...
16965 queue.enqueueOrderedBroadcastLocked(r);
16966 queue.scheduleBroadcastsLocked();
...
16968 }
ActivityManagerService.broadcastIntentLocked完整流程分析
下面我們就要正式開始看後面的處理邏輯了。先給大家來看一張時序圖,整體上先有一個印象:
sequenceDiagram;
Activity ->> ContextWrapper : sendBroadcast()
ContextWrapper ->> ContextImpl : sendBroadcast()
ContextImpl ->> ActivityManagerService: broadcastIntent()
ActivityManagerService ->> ActivityManagerService : broadcastIntentLocked()
ActivityManagerService ->> ActivityManagerService : collectReceiverComponents()
ActivityManagerService ->> BroadcastQueue : scheduleBroadcastsLocked()
BroadcastQueue ->> BroadcastQueue : processNextBroadcast()
ActivityManagerService ->> ActivityManagerService : deliverToRegisteredReceiverLocked()
ActivityManagerService ->> ActivityManagerService : performReceiveLocked()
ActivityManagerService ->> ApplicationThreadProxy : scheduleRegisteredReceiver()
ApplicationThreadProxy ->> InnerReceiver : performReceive()
InnerReceiver ->> ReceiverDispatcher : performReceive()
ReceiverDispatcher ->> BroadcastReceiver : onReceive()
上面我們是跳著看的,大家缺少一個完整的印象,讓我們耐著性子把整個流程過一下吧。
private final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle options,
boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
intent = new Intent(intent);
// By default broadcasts do not go to stopped apps.
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);
}
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
(sticky ? "Broadcast sticky: ": "Broadcast: ") + intent
+ " ordered=" + ordered + " userid=" + userId);
if ((resultTo != null) && !ordered) {
Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
}
userId = handleIncomingUser(callingPid, callingUid, userId,
true, ALLOW_NON_FULL, "broadcast", callerPackage);
...
中間是一堆判斷許可權之類的操作,我們就略過不看了。
...
final String action = intent.getAction();
if (action != null) {
switch (action) {
case Intent.ACTION_UID_REMOVED:
case Intent.ACTION_PACKAGE_REMOVED:
case Intent.ACTION_PACKAGE_CHANGED:
case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
...
case Intent.ACTION_TIMEZONE_CHANGED:
// If this is the time zone changed action, queue up a message that will reset
// the timezone of all currently running processes. This message will get
// queued up before the broadcast happens.
mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);
break;
case Intent.ACTION_TIME_CHANGED:
// If the user set the time, let all running processes know.
final int is24Hour =
intent.getBooleanExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, false) ? 1
: 0;
mHandler.sendMessage(mHandler.obtainMessage(UPDATE_TIME, is24Hour, 0));
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
synchronized (stats) {
stats.noteCurrentTimeChangedLocked();
}
break;
case Intent.ACTION_CLEAR_DNS_CACHE:
mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);
break;
case Proxy.PROXY_CHANGE_ACTION:
ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO);
mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));
break;
}
}
...
上面是一些特殊的廣播的處理。
下面終於開始比較核心的邏輯了,去查詢一下接收這個廣播的接收者都是些什麼吧。
// 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) {
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
FLAG_RECEIVER_REGISTERED_ONLY這個屬性用於說明是否是支援在程式碼中動態註冊的接收者,如果不是的話,那麼要通過collectReceiverComponents方法去查一下哪些應用在AndroidManifest.xml中也註冊了監聽。
這麼有趣的函式,我們跟起去看看吧。
ActivityManagerService.collectReceiverComponents
進入之前,我們先看看幾個資料類的定義:
ResolveInfo類用來儲存AndroidManifest.xml中<intent>
標籤中對應的資訊。
public class ResolveInfo implements Parcelable;
我們正式開始進入collectReceiverComponents:
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,int callingUid, int[] users) {
List<ResolveInfo> receivers = null;
try {
HashSet<ComponentName> singleUserReceivers = null;
boolean scannedFirstReceivers = false;
for (int user : users) {
// Skip users that have Shell restrictions
if (callingUid == Process.SHELL_UID
&& getUserManagerLocked().hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES, user)) {
continue;
}
下面呼叫到PMS去查詢:
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
.queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);
PMS中的這段程式碼是這樣的,我們先不再往下細分析了,後面會講註冊的時候再提到。
public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags,
int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) {
List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
ActivityInfo ai = getReceiverInfo(comp, flags, userId);
if (ai != null) {
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
return list;
}
// reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
return mReceivers.queryIntent(intent, resolvedType, flags, userId);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers,
userId);
}
return null;
}
}
我們回到collectReceiverComponents中繼續往下看:
if (user != UserHandle.USER_OWNER && newReceivers != null) {
// If this is not the primary user, we need to check for
// any receivers that should be filtered out.
for (int i=0; i<newReceivers.size(); i++) {
ResolveInfo ri = newReceivers.get(i);
if ((ri.activityInfo.flags&ActivityInfo.FLAG_PRIMARY_USER_ONLY) != 0) {
newReceivers.remove(i);
i--;
}
}
}
if (newReceivers != null && newReceivers.size() == 0) {
newReceivers = null;
}
if (receivers == null) {
receivers = newReceivers;
} else if (newReceivers != null) {
// We need to concatenate the additional receivers
// found with what we have do far. This would be easy,
// but we also need to de-dup any receivers that are
// singleUser.
if (!scannedFirstReceivers) {
// Collect any single user receivers we had already retrieved.
scannedFirstReceivers = true;
for (int i=0; i<receivers.size(); i++) {
ResolveInfo ri = receivers.get(i);
if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
ComponentName cn = new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name);
if (singleUserReceivers == null) {
singleUserReceivers = new HashSet<ComponentName>();
}
singleUserReceivers.add(cn);
}
}
}
// Add the new results to the existing results, tracking
// and de-dupping single user receivers.
for (int i=0; i<newReceivers.size(); i++) {
ResolveInfo ri = newReceivers.get(i);
if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
ComponentName cn = new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name);
if (singleUserReceivers == null) {
singleUserReceivers = new HashSet<ComponentName>();
}
if (!singleUserReceivers.contains(cn)) {
singleUserReceivers.add(cn);
receivers.add(ri);
}
} else {
receivers.add(ri);
}
}
}
}
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
return receivers;
}
下面,我們回到broadcastIntentLocked中繼續往下看:
if (intent.getComponent() == null) {
if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
// Query one target user at a time, excluding shell-restricted users
UserManagerService ums = getUserManagerLocked();
for (int i = 0; i < users.length; i++) {
if (ums.hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
continue;
}
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);
}
}
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;
if (!ordered && NR > 0) {
// If we are not serializing this broadcast, then send the
// registered receivers separately so they don't wait for the
// components to be launched.
如果不是ordered,且註冊這個事件的receiver佇列不為空,我們就開始處理普通訊息了!
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);
下面我們就將普通訊息加入到並行訊息佇列裡,enqueueParallelBroadcastLocked和scheduleBroadcastsLocked我們前面預備知識都講過了,這裡不再重複了。
if (!replaced) {
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}
以上,併發訊息處理正式結束。下面開始的是序列訊息的處理,中間我們略去一段對於PACKAGE_ADDED訊息發給新安裝的應用這一段支線任務。
另外,有序佇列中還有一件額外的事情需要做,有序麼,要排序。
// Merge into one list.
int ir = 0;
if (receivers != 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++;
}
最後是加入到序列佇列併傳送訊息的過程,跟併發佇列除了佇列不同,其餘都是一樣的。
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();
}
}
return ActivityManager.BROADCAST_SUCCESS;
}
相關文章
- Android開機廣播和關機廣播Android
- Android中的廣播使用Android
- 說說Android的MVP模式AndroidMVP模式
- Android BroadcastReceiver(廣播)AndroidAST
- Android - BroadcastReceiver 廣播AndroidAST
- Android廣播動作Android
- Android複習–廣播Android
- android: 廣播機制Android
- android: 使用本地廣播Android
- 說說Android上的事件傳遞Android事件
- 說服的傳播模型(轉載)模型
- Android系統廣播(轉)Android
- Android之Broadcast(廣播)AndroidAST
- android廣播集合,intent,actionAndroidIntent
- android: 接收系統廣播Android
- Android之粘性廣播理解Android
- android藍芽BLE(三) —— 廣播Android藍芽
- android: 傳送自定義廣播Android
- 為什麼說Java的就業面廣?Java就業
- Android廣播之靜態註冊Android
- Android 廣播許可權保護Android
- Android利用廣播攔截簡訊Android
- 說說在 Android 的 RecyclerView 中如何實現下拉刷AndroidView
- 組播和廣播的區別
- 單播、多播(組播)和廣播的區別
- 廣播接收器——接收系統廣播
- 細說 Android 的 MVP 模式AndroidMVP模式
- Android利用廣播進行IP撥號Android
- 廣播模式模式
- Android開發——說說Adapter那點事AndroidAPT
- 說說Android動態換膚實現原理吧Android
- Android 外掛化原理解析(7):廣播的管理Android
- 說說java的反射Java反射
- Android-Broadcast Receiver(廣播接收器)AndroidAST
- Android入門教程 | 廣播機制 BroadcastAndroidAST
- Android開機和關機廣播監聽Android
- iOS下的UDP廣播iOSUDP
- 2020怎麼做IP運營推廣?我來說說-莫安迪