咱們先隨便實現一個BroadcastReceiver,靜態註冊:java
class TestReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.w("TEST-1", "onReceive ${intent?.action}")
}
}
複製代碼
<receiver android:name=".TestReceiver">
<intent-filter>
<action android:name="com.xxx.yyy.action_test_receiver" />
</intent-filter>
</receiver>
複製代碼
其餘諸如Activity什麼的就不寫了哈,而後咱們啓動這個測試App以後,用adb命令發一條廣播:android
adb shell am broadcast -p com.xxx.yyy -a com.xxx.yyy.action_test_receiver
複製代碼
其中參數p表示廣播接收所在進程包名,a表示action。命令執行後終端會輸出:shell
Broadcasting: Intent { act=com.xxx.yyy.action_test_receiver flg=0x400000 pkg=com.xxx.yyy }
Broadcast completed: result=0
複製代碼
而後查看logcat,咱們能夠如願以償地看到onReceive中的log。接下來咱們殺掉進程,任意方式都可,這裏我仍是用adb命令,方便:安全
adb shell am force-stop com.xxx.yyy
複製代碼
殺進程後,再重複上面的廣播發送命令,就會發現收不到廣播了。這是爲何呢?表面看來這個問題很弱智,進程都死了固然不能再搞事。但實際上背後的邏輯仍是值得探索的,系統也不是想象中那麼簡單地直接判斷進程死活而後決定廣播發送。bash
很顯然咱們要搞明白廣播發送的底層邏輯。這裏主要分析Framework層面的源碼(基於Android 10),涉及到的關鍵類:app
frameworks/base/core/java/android/content/Intent.java
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
frameworks/base/services/core/java/com/android/server/pm/ComponentResolver.java
frameworks/base/services/core/java/com/android/server/IntentResolver.java
複製代碼
關於廣播發送的細節,能夠參考此文:www.jianshu.com/p/c5323a22f…,雖然源碼版本不是最新,但基本邏輯差別不大,時序圖也畫得很是清晰。下面咱們只針對文題簡單地分析關鍵路徑便可。ide
當咱們樂呵呵地調用了 sendBroadcast
方法以後,會調用到AMS(ActivityManagerService)的 broadcastIntent
方法,進而調用內部的 broadcastIntentLocked
方法(此處插入一個題外話:不少同窗可能常常見到源碼裏 xxxLocked
這種方法,這個locked是什麼意思呢?顧名思義就是加鎖咯,即你要調用這些locked後綴的方法時,必須保證是線程安全的,因此通常就會看到synchronize關鍵字,這也算是AOSP的編碼規範吧)。測試
因爲 broadcastIntentLocked
方法很是長,咱們只截取關鍵片斷:優化
final int broadcastIntentLocked(Intent intent/*省略18個參數*/) {
intent = new Intent(intent);
// ...
// By default broadcasts do not go to stopped apps.
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
// ...
// 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);
}
// ...
}
複製代碼
能夠看到,起手就是new Intent(intent),爲何不直接使用參數中的intent來進行後續操做呢?此處一個小細節能夠看出源碼邏輯的謹慎,去查Intent的構造方法就知道,這是對入參的拷貝,避免被其餘線程修改。 而後最關鍵的即是下面的 FLAG_EXCLUDE_STOPPED_PACKAGES
,註釋也寫得很清楚,即廣播發送會排除(exclude)已中止運行的進程。ui
但我初次分析時看了半天沒發現是怎麼排除的,因而找這個flag引用的地方,在 Intent 源碼中發現一個方法,判斷該intent是否要排除已中止的進程:
public boolean isExcludingStopped() {
return (mFlags&(FLAG_EXCLUDE_STOPPED_PACKAGES|FLAG_INCLUDE_STOPPED_PACKAGES))
== FLAG_EXCLUDE_STOPPED_PACKAGES;
}
複製代碼
很是好,咱們直接查 isExcludingStopped
方法的引用,發如今 IntentResolver 中:
private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories, boolean debug, boolean defaultOnly, String resolvedType, String scheme, F[] src, List<R> dest, int userId) {
// ...
final boolean excludingStopped = intent.isExcludingStopped();
// ...
for (i=0; i<N && (filter=src[i]) != null; i++) {
// ...
if (excludingStopped && isFilterStopped(filter, userId)) {
if (debug) {
Slog.v(TAG, " Filter's target is stopped; skipping");
}
continue;
}
}
// ...
}
複製代碼
這是經過intent構造resolve列表的一個私有方法,代碼也很是清晰,此判斷邏輯 excludingStopped && isFilterStopped
過濾了最後要啓動的組件。其中 isFilterStopped
是真正判斷進程是否已中止的方法,而 excludingStopped 是咱們剛纔傳入的flag對應的控制標識。因此要過濾掉已中止進程有兩個必要條件,一是這個進程真的死了,二是開發者要經過flag來聲明確實須要過濾(是否是超人性化哈哈哈)。
所以,咱們也能夠聲明不須要過濾,即給intent設置 FLAG_EXCLUDE_STOPPED_PACKAGES
的兄弟flag:FLAG_INCLUDE_STOPPED_PACKAGES
(注意是 include),這樣在發送廣播時,即使是已中止的進程,也能接收到了。這就是上古時期經過廣播喚醒死亡進程的方法,如今基本上被各大ROM廠商給優化沒了,通常都是三方應用被禁,系統應用依然能夠。
分析到此,其實只是有頭有尾,但沒有中間過程,broadcastIntentLocked
最終怎麼就調到了 buildResolveList
呢? 咱們回到上面的 broadcastIntentLocked
方法,其中的 receivers
對象存儲的是靜態廣播的集合,registeredReceivers
則是動態廣播的集合。咱們只看靜態廣播便可,它來自於 collectReceiverComponents
方法,此方法最後返回的receiver確定是被過濾後的:
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType, int callingUid, int[] users) {
// ...
List<ResolveInfo> receivers = null;
try {
// ...
for (int user : users) {
// ...
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
.queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
// ...
if (newReceivers != null && newReceivers.size() == 0) {
newReceivers = null;
}
if (receivers == null) {
receivers = newReceivers;
// ...
}
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
return receivers;
}
複製代碼
這個方法內部邏輯較爲簡單,咱們能夠看到最終receivers的來源即是那個 queryIntentReceivers
方法,此方法實如今PMS(PackageManagerService)裏面:
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) {
return new ParceledListSlice<>(
queryIntentReceiversInternal(intent, resolvedType, flags, userId,
false /*allowDynamicSplits*/));
}
private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent, String resolvedType, int flags, int userId, boolean allowDynamicSplits) {
// ...
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
final List<ResolveInfo> result =
mComponentResolver.queryReceivers(intent, resolvedType, flags, userId);
return applyPostResolutionFilter(
result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
intent);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
final List<ResolveInfo> result = mComponentResolver.queryReceivers(
intent, resolvedType, flags, pkg.receivers, userId);
return applyPostResolutionFilter(
result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
intent);
}
return Collections.emptyList();
}
}
複製代碼
最終receiver集合的查詢操做在私有方法中,由 mComponentResolver.queryReceivers
得來,彷佛愈來愈接近真相了,立刻查看ComponentResolver:
List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, int flags, int userId) {
synchronized (mLock) {
return mReceivers.queryIntent(intent, resolvedType, flags, userId);
}
}
List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, int flags, List<PackageParser.Activity> receivers, int userId) {
synchronized (mLock) {
return mReceivers.queryIntentForPackage(intent, resolvedType, flags, receivers, userId);
}
}
複製代碼
這裏正好對應上面的兩個不一樣狀況下的調用,它們最終都會調用到 queryIntent
方法,此方法在ComponentResolver的一個靜態內部類ActivityIntentResolver中實現:
private static final class ActivityIntentResolver extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
@Override
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, int userId) {
// ...
return super.queryIntent(intent, resolvedType, defaultOnly, userId);
}
List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags, int userId) {
// ...
return super.queryIntent(intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
userId);
}
// ...
}
複製代碼
內部類的中的 queryIntent
方法只是作了一些參數處理,進一步調用的是父類的實現,這個父類也就是起初咱們提到的 IntentResolver:
public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, int userId) {
// ...
if (firstTypeCut != null) {
buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
scheme, firstTypeCut, finalList, userId);
}
if (secondTypeCut != null) {
buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
scheme, secondTypeCut, finalList, userId);
}
if (thirdTypeCut != null) {
buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
scheme, thirdTypeCut, finalList, userId);
}
if (schemeCut != null) {
buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
scheme, schemeCut, finalList, userId);
}
filterResults(finalList);
sortResults(finalList);
// ...
return finalList;
}
複製代碼
看上面的 buildResolveList
方法,照應了開頭分析的結果。最終返回的 finalList 也就是過濾以後的receiver集合。
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
設置了標識,以聲明廣播不發給已中止的進程,層層調用後最終在 IntentResolver 的 buildResolveList
方法中實現過濾。sendBroadcast
以前,給intent添加 flag:FLAG_INCLUDE_STOPPED_PACKAGES
便可。但鑑於各ROM廠商的正負優化,這個操做已經不適用了。