通過廣播Intent來查找對(duì)應(yīng)廣播接收者的具體實(shí)現(xiàn)
上一篇我們粗略的走完了一遍廣播發(fā)送的主流程,但一些詳細(xì)的具體實(shí)現(xiàn)沒有仔細(xì)研讀露该,所以接下來我們要一點(diǎn)一點(diǎn)的給補(bǔ)回去底靠。這個(gè)篇章我們主要研究廣播意圖和廣播接收者的配對(duì)查找,看看谷歌究竟是怎么實(shí)現(xiàn)的!
上一篇文章我們說到廣播接收者查詢分為了靜態(tài)廣播查詢和動(dòng)態(tài)廣播查詢询筏,此次我們先分析靜態(tài)廣播接收者查詢。
切入入口就從廣播發(fā)送的主流程那邊開始帚稠,首先我們先分析collectReceiverComponents方法的內(nèi)部實(shí)現(xiàn):
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
...
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
int callingUid, int[] users) {
// TODO: come back and remove this assumption to triage all broadcasts
int pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;
List<ResolveInfo> receivers = null;
try {
HashSet<ComponentName> singleUserReceivers = null;
boolean scannedFirstReceivers = false;
for (int user : users) {//循環(huán)獲取各用戶下的廣播接收者
// Skip users that have Shell restrictions, with exception of always permitted
//跳過具有Shell限制的用戶
// Shell broadcasts
if (callingUid == SHELL_UID
&& mUserController.hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES, user)
&& !isPermittedShellBroadcast(intent)) {
continue;
}
//查找匹配Intent 的廣播接收者
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
.queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
if (user != UserHandle.USER_SYSTEM && newReceivers != null) {
// If this is not the system user, we need to check for
// any receivers that should be filtered out.
//如果不是系統(tǒng)用戶影兽,檢查過濾廣播接收者
for (int i=0; i<newReceivers.size(); i++) {
ResolveInfo ri = newReceivers.get(i);
if ((ri.activityInfo.flags&ActivityInfo.FLAG_SYSTEM_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) {
//掃描第一個(gè)用戶對(duì)應(yīng)的廣播接收者拇砰,并篩選出單個(gè)用戶廣播接收者
// 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.
//循環(huán)遍歷新的接收者列表
for (int i=0; i<newReceivers.size(); i++) {
ResolveInfo ri = newReceivers.get(i);
if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
//篩選出單個(gè)用戶廣播接收者甥材,避免多次發(fā)送
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;
}
...
}
沿著AppGlobals.getPackageManager().queryIntentReceivers()走下去,最終你會(huì)發(fā)現(xiàn)靜態(tài)廣播接收者的查詢是在系統(tǒng)包管理服務(wù)PackageManagerService 里面實(shí)現(xiàn)的赫蛇。
public class PackageManagerService extends IPackageManager.Stub
implements PackageSender {
...
public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
String resolvedType, int flags, int userId) {
return new ParceledListSlice<>(
queryIntentReceiversInternal(intent, resolvedType, flags, userId));
}
private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
String resolvedType, int flags, int userId) {
//用戶是否存在校驗(yàn)
if (!sUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, intent, callingUid,
false /*includeInstantApps*/);
//獲取廣播接收者組件信息移迫,包括包名和類名
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) {//組件不為空,則說明不用去查找,直接可以組裝并返回ResolveInfo
final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
//獲取此次活動(dòng)的相關(guān)信息
final ActivityInfo ai = getReceiverInfo(comp, flags, userId);
if (ai != null) {
// When specifying an explicit component, we prevent the activity from being
// used when either 1) the calling package is normal and the activity is within
// an instant application or 2) the calling package is ephemeral and the
// activity is not visible to instant applications.
//兩個(gè)明確的情況下叮姑,此次活動(dòng)將被阻止
//1托享、調(diào)用包正常传惠,活動(dòng)在Instant APP中運(yùn)行
//2浇坐、調(diào)用包是臨時(shí)的摇予,活動(dòng)對(duì)Instant APP不可見。
final boolean matchInstantApp =
(flags & PackageManager.MATCH_INSTANT) != 0;
final boolean matchVisibleToInstantAppOnly =
(flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
final boolean matchExplicitlyVisibleOnly =
(flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
final boolean isCallerInstantApp =
instantAppPkgName != null;
final boolean isTargetSameInstantApp =
comp.getPackageName().equals(instantAppPkgName);
final boolean isTargetInstantApp =
(ai.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
final boolean isTargetVisibleToInstantApp =
(ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
final boolean isTargetExplicitlyVisibleToInstantApp =
isTargetVisibleToInstantApp
&& (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
final boolean isTargetHiddenFromInstantApp =
!isTargetVisibleToInstantApp
|| (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp);
final boolean blockResolution =
!isTargetSameInstantApp
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
if (!blockResolution) {
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
}
return applyPostResolutionFilter(list, instantAppPkgName);//過濾臨時(shí)活動(dòng)
}
// reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
//通過mReceivers查詢獲取ResolveInfo列表
final List<ResolveInfo> result =
mReceivers.queryIntent(intent, resolvedType, flags, userId);
return applyPostResolutionFilter(result, instantAppPkgName);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
//查詢mReceivers獲取ResolveInfo列表
final List<ResolveInfo> result = mReceivers.queryIntentForPackage(
intent, resolvedType, flags, pkg.receivers, userId);
return applyPostResolutionFilter(result, instantAppPkgName);
}
return Collections.emptyList();
}
}
private List<ResolveInfo> applyPostResolutionFilter(List<ResolveInfo> resolveInfos,
String ephemeralPkgName) {
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
final boolean isEphemeralApp = info.activityInfo.applicationInfo.isInstantApp();
// TODO: When adding on-demand split support for non-instant apps, remove this check
// and always apply post filtering
// allow activities that are defined in the provided package
if (isEphemeralApp) {
if (info.activityInfo.splitName != null
&& !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames,
info.activityInfo.splitName)) {
// requested activity is defined in a split that hasn't been installed yet.
// add the installer to the resolve list
if (DEBUG_EPHEMERAL) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo);
installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
info.activityInfo.packageName, info.activityInfo.splitName,
info.activityInfo.applicationInfo.versionCode, null /*failureIntent*/);
// make sure this resolver is the default
installerInfo.isDefault = true;
installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
// add a non-generic filter
installerInfo.filter = new IntentFilter();
// load resources from the correct package
installerInfo.resolvePackageName = info.getComponentInfo().packageName;
resolveInfos.set(i, installerInfo);
continue;
}
}
// caller is a full app, don't need to apply any other filtering
if (ephemeralPkgName == null) {
continue;
} else if (ephemeralPkgName.equals(info.activityInfo.packageName)) {
// caller is same app; don't need to apply any other filtering
continue;
}
// allow activities that have been explicitly exposed to ephemeral apps
if (!isEphemeralApp
&& ((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) {
continue;
}
resolveInfos.remove(i);
}
return resolveInfos;
}
...
}
從上面的代碼邏輯來看吗跋,在發(fā)送廣播時(shí)把對(duì)應(yīng)的包名和類名帶上,廣播發(fā)送的效率會(huì)有很大的提高,因?yàn)槠渲腥鄙倭俗詈臅r(shí)的Intent查找匹配邏輯跌宛!而最終的匹配查找是在變量mReceivers中實(shí)現(xiàn)的酗宋,那么mReceivers究竟做了什么呢?
public class PackageManagerService extends IPackageManager.Stub
implements PackageSender {
...
// All available receivers, for your resolving pleasure.
final ActivityIntentResolver mReceivers = new ActivityIntentResolver();
final class ActivityIntentResolver
extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
....
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
int userId) {
if (!sUserManager.exists(userId)) return null;
mFlags = flags;
return super.queryIntent(intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
userId);
}
public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
int flags, ArrayList<PackageParser.Activity> packageActivities, int userId) {
if (!sUserManager.exists(userId)) return null;
if (packageActivities == null) {
return null;
}
mFlags = flags;
final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
final int N = packageActivities.size();
ArrayList<PackageParser.ActivityIntentInfo[]> listCut =
new ArrayList<PackageParser.ActivityIntentInfo[]>(N);
ArrayList<PackageParser.ActivityIntentInfo> intentFilters;
for (int i = 0; i < N; ++i) {
intentFilters = packageActivities.get(i).intents;
if (intentFilters != null && intentFilters.size() > 0) {
PackageParser.ActivityIntentInfo[] array =
new PackageParser.ActivityIntentInfo[intentFilters.size()];
intentFilters.toArray(array);
listCut.add(array);
}
}
return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
}
....
}
...
}
沒錯(cuò)疆拘,事實(shí)上的查找邏輯是寫在父類IntentResolver里面的:
public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
....
public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
int userId) {
String scheme = intent.getScheme();
ArrayList<R> finalList = new ArrayList<R>();
final boolean debug = localLOGV ||
((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
if (debug) Slog.v(
TAG, "Resolving type=" + resolvedType + " scheme=" + scheme
+ " defaultOnly=" + defaultOnly + " userId=" + userId + " of " + intent);
F[] firstTypeCut = null;
F[] secondTypeCut = null;
F[] thirdTypeCut = null;
F[] schemeCut = null;
// If the intent includes a MIME type, then we want to collect all of
// the filters that match that MIME type.
//通過MIME type來匹配Intent
if (resolvedType != null) {
int slashpos = resolvedType.indexOf('/');
if (slashpos > 0) {
final String baseType = resolvedType.substring(0, slashpos);
if (!baseType.equals("*")) {
if (resolvedType.length() != slashpos+2
|| resolvedType.charAt(slashpos+1) != '*') {
// Not a wild card, so we can just look for all filters that
// completely match or wildcards whose base type matches.
firstTypeCut = mTypeToFilter.get(resolvedType);
if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut));
secondTypeCut = mWildTypeToFilter.get(baseType);
if (debug) Slog.v(TAG, "Second type cut: "
+ Arrays.toString(secondTypeCut));
} else {
// We can match anything with our base type.
firstTypeCut = mBaseTypeToFilter.get(baseType);
if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut));
secondTypeCut = mWildTypeToFilter.get(baseType);
if (debug) Slog.v(TAG, "Second type cut: "
+ Arrays.toString(secondTypeCut));
}
// Any */* types always apply, but we only need to do this
// if the intent type was not already */*.
thirdTypeCut = mWildTypeToFilter.get("*");
if (debug) Slog.v(TAG, "Third type cut: " + Arrays.toString(thirdTypeCut));
} else if (intent.getAction() != null) {
// The intent specified any type ({@literal *}/*). This
// can be a whole heck of a lot of things, so as a first
// cut let's use the action instead.
firstTypeCut = mTypedActionToFilter.get(intent.getAction());
if (debug) Slog.v(TAG, "Typed Action list: " + Arrays.toString(firstTypeCut));
}
}
}
// If the intent includes a data URI, then we want to collect all of
// the filters that match its scheme (we will further refine matches
// on the authority and path by directly matching each resulting filter).
//通過scheme來匹配
if (scheme != null) {
schemeCut = mSchemeToFilter.get(scheme);
if (debug) Slog.v(TAG, "Scheme list: " + Arrays.toString(schemeCut));
}
// If the intent does not specify any data -- either a MIME type or
// a URI -- then we will only be looking for matches against empty
// data.
//通過action來匹配
if (resolvedType == null && scheme == null && intent.getAction() != null) {
firstTypeCut = mActionToFilter.get(intent.getAction());
if (debug) Slog.v(TAG, "Action list: " + Arrays.toString(firstTypeCut));
}
//將查找出來的封裝成為目標(biāo)對(duì)象列表并返回
FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
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);
if (debug) {
Slog.v(TAG, "Final result list:");
for (int i=0; i<finalList.size(); i++) {
Slog.v(TAG, " " + finalList.get(i));
}
}
return finalList;
}
....
/**
* All filters that have been registered.
*/
private final ArraySet<F> mFilters = new ArraySet<F>();
/**
* All of the MIME types that have been registered, such as "image/jpeg",
* "image/*", or "{@literal *}/*".
*/
private final ArrayMap<String, F[]> mTypeToFilter = new ArrayMap<String, F[]>();
/**
* The base names of all of all fully qualified MIME types that have been
* registered, such as "image" or "*". Wild card MIME types such as
* "image/*" will not be here.
*/
private final ArrayMap<String, F[]> mBaseTypeToFilter = new ArrayMap<String, F[]>();
/**
* The base names of all of the MIME types with a sub-type wildcard that
* have been registered. For example, a filter with "image/*" will be
* included here as "image" but one with "image/jpeg" will not be
* included here. This also includes the "*" for the "{@literal *}/*"
* MIME type.
*/
private final ArrayMap<String, F[]> mWildTypeToFilter = new ArrayMap<String, F[]>();
/**
* All of the URI schemes (such as http) that have been registered.
*/
private final ArrayMap<String, F[]> mSchemeToFilter = new ArrayMap<String, F[]>();
/**
* All of the actions that have been registered, but only those that did
* not specify data.
*/
private final ArrayMap<String, F[]> mActionToFilter = new ArrayMap<String, F[]>();
/**
* All of the actions that have been registered and specified a MIME type.
*/
private final ArrayMap<String, F[]> mTypedActionToFilter = new ArrayMap<String, F[]>();
}
從上面的代碼可以看出蜕猫,查詢廣播的目標(biāo)接收者其實(shí)是通過Map鍵值對(duì)來查找的,根據(jù)不同的專屬特征(Action哎迄、Scheme回右、Mime Typed等)來獲取對(duì)應(yīng)接收者,邏輯其實(shí)并不復(fù)雜漱挚。代碼讀到此處翔烁,我們難免會(huì)有疑問:mActionToFilter、mSchemeToFilter等這些過濾器是什么時(shí)候初始化的旨涝?靜態(tài)注冊(cè)的廣播是如何加載到這些Map里面的蹬屹?別急,代碼還需繼續(xù)看下去:
public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
....
public void addFilter(F f) {
if (localLOGV) {
Slog.v(TAG, "Adding filter: " + f);
f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
Slog.v(TAG, " Building Lookup Maps:");
}
mFilters.add(f);
int numS = register_intent_filter(f, f.schemesIterator(),
mSchemeToFilter, " Scheme: ");
int numT = register_mime_types(f, " Type: ");
if (numS == 0 && numT == 0) {
register_intent_filter(f, f.actionsIterator(),
mActionToFilter, " Action: ");
}
if (numT != 0) {
register_intent_filter(f, f.actionsIterator(),
mTypedActionToFilter, " TypedAction: ");
}
}
private final int register_intent_filter(F filter, Iterator<String> i,
ArrayMap<String, F[]> dest, String prefix) {
if (i == null) {
return 0;
}
int num = 0;
while (i.hasNext()) {
String name = i.next();
num++;
if (localLOGV) Slog.v(TAG, prefix + name);
addFilter(dest, name, filter);
}
return num;
}
private final void addFilter(ArrayMap<String, F[]> map, String name, F filter) {
F[] array = map.get(name);
if (array == null) {
array = newArray(2);
map.put(name, array);
array[0] = filter;
} else {
final int N = array.length;
int i = N;
while (i > 0 && array[i-1] == null) {
i--;
}
if (i < N) {
array[i] = filter;
} else {
F[] newa = newArray((N*3)/2);
System.arraycopy(array, 0, newa, 0, N);
newa[N] = filter;
map.put(name, newa);
}
}
}
}
全局查看IntentResolver的代碼白华,你會(huì)發(fā)現(xiàn)IntentResolver對(duì)外開放的過濾器數(shù)據(jù)添加接口只有一個(gè)addFilter(F f)慨默,而mReceivers中將這個(gè)接口的調(diào)用封裝到了addActivity(PackageParser.Activity a, String type)方法中,沿著方法被調(diào)用的地方查找弧腥,你會(huì)發(fā)現(xiàn)PackageManagerService的構(gòu)造函數(shù)中經(jīng)過一層層的封裝厦取,最終有觸發(fā)IntentResolver.addActivity()的調(diào)用,此處應(yīng)是用作初始化管搪;同時(shí)在PackageHandler mHandler 中也有觸發(fā)調(diào)用虾攻,此處應(yīng)是用作更新!此處代碼較多就不一一摘錄了抛蚤。
總結(jié)
通過上面的源碼分析台谢,我們可以總結(jié)出一下結(jié)論:
1、靜態(tài)廣播接收者的查找實(shí)際上是在包管理服務(wù)PackageManagerService 中實(shí)現(xiàn)的岁经。
2朋沮、廣播Intent中指定目標(biāo)接收者的包名和類名會(huì)跳過查找的步驟,極大的提高了廣播發(fā)送的效率缀壤。
3樊拓、靜態(tài)廣播接收者會(huì)在包管理服務(wù)PackageManagerService初始化的時(shí)候以鍵值對(duì)的形式被加載到內(nèi)存當(dāng)中,廣播Intent在查找對(duì)應(yīng)接收者的時(shí)候通過其所帶的Action塘慕、Scheme筋夏、MIME TYPE等數(shù)據(jù)來獲取對(duì)應(yīng)的接收者。當(dāng)然在有新android包安裝图呢、升級(jí)或卸載的時(shí)候条篷,靜態(tài)廣播接收者的數(shù)據(jù)也會(huì)實(shí)時(shí)更新骗随。