通过前面的分析,我们知道PKMS负责维护终端全部的Package信息,因此可以想到PKMS具有能力对外提供统一的Package信息查询接口。
我们以查询匹配指定Intent的所有Activity的过程为例,分析一下PKMS提供这类服务对应的流程。
一、PKMS中Activity的信息管理
在前面的博客已经提到,PKMS解析Package信息时,将调用到scanPackageDirtyLI函数。
该函数将会将Package中四大组件等信息,加入到PKMS定义的数据结构中,以便统一管理。
以下代码片段就是为了将Package中的activity相关的信息,加入到PKMS中:
N = pkg.activities.size();
r = null;
for (i=0; i
PackageParser.Activity a = pkg.activities.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
//Package中的信息被加入到了PKMS的mActivities中
mActivities.addActivity(a, "activity");
..........
}
上述代码的内容比较简单,主要需要关注对应的数据结构。
结合代码,我们知道:
1、PKMS中的成员变量mActivities为ActivityIntentResolver类型,用于保存所有与Activity相关的信息。ActivityIntentResolver内部也有一个mActivities变量,它以ComponentName为key,保存PackageParser.Activity对象。
2、从APK文件中解析出来的所有和Activity相关的信息都由PackageParser.Activity来保存。
我们跟进一下ActivityIntentResolver的addActivity函数:
public final void addActivity(PackageParser.Activity a, String type) {
//将ComponentName和Activity保存到ActivityIntentResolver的mActivities中
mActivities.put(a.getComponentName(), a);
..........
final int NI = a.intents.size();
for (int j=0; j<NI; j++) {
//ActivityIntentInfo存储的是XML中声明的IntentFilter信息
PackageParser.ActivityIntentInfo intent = a.intents.get(j);
if ("activity".equals(type)) {
final PackageSetting ps =
mSettings.getDisabledSystemPkgLPr(intent.activity.info.packageName);
final List<PackageParser.Activity> systemActivities =
ps != null && ps.pkg != null ? ps.pkg.activities : null;
//按照策略,将一些IntentFilter的优先级设置为0
//例如非系统APK的优先级将被设置为0
adjustPriority(systemActivities, intent);
}
..........
//将activity中对应的IntentFilter加入到PKMS中
addFilter(intent);
}
}
从上面的代码,我们知道了PKMS除了维护Activity的基本信息外,重点“照顾”了Activity定义的IntentFilter。
跟进addFilter函数:
public void addFilter(F f) {
.......
//mFilters保存所有的IntentFilter信息
mFilters.add(f);
//除此之外,为了加快匹配工作的速度,PKMS还分类保存了IntentFilter的信息
//mSchemeToFilter用于保存uri中与scheme相关的IntentFilter
int numS = register_intent_filter(f, f.schemesIterator(),
mSchemeToFilter, " Scheme: ");
//按照MIME type存储IntentFilter,分别定义了mTypeToFilter、mBaseTypeToFilter和mWildTypeToFilter
//与MIME的格式有关,代码较为简单,不深入分析
int numT = register_mime_types(f, " Type: ");
if (numS == 0 && numT == 0) {
//mActionToFilter用于保存仅设置了Action条件的IntentFilter
register_intent_filter(f, f.actionsIterator(),
mActionToFilter, " Action: ");
}
if (numT != 0) {
//mTypedActionToFilter用于保存既设置了Action条件,又设置了Data类型的IntentFilter
register_intent_filter(f, f.actionsIterator(),
mTypedActionToFilter, " TypedAction: ");
}
}
二、PKMS中Intent与Activity的匹配过程
一般情况下,客户端可以使用ApplicationPackageManager的接口queryIntentActivities发起查询请求:
public List queryIntentActivities(Intent intent, int flags) {
return queryIntentActivitiesAsUser(intent, flags, mContext.getUserId());
}
public List queryIntentActivitiesAsUser(Intent intent,
int flags, int userId) {
try {
ParceledListSlice parceledList =
//调用PKMS的接口
mPM.queryIntentActivities(intent,
//如果Intent的Data中包含Uri,那么就需要根据Uri对应的ContentProvider得到Data对应的类型
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags, userId);
if (parceledList == null) {
return Collections.emptyList();
}
return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
我们主要分析PKMS的queryIntentActivities函数:
public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
return new ParceledListSlice<>(
queryIntentActivitiesInternal(intent, resolvedType, flags, userId));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(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) {
final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
//利用getActivityInfo获取指定的ActivityInfo即可
final ActivityInfo ai = getActivityInfo(comp, flags, userId);
if (ai != null) {
final ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
return list;
}
synchronized (mPackages) {
final String pkgName = intent.getPackage();
//Intent未指定Package名,需要在整个系统范围内进行匹配查询
if (pkgName == null) {
//处理隐式Intent,此处引入了CrossProfile和Current Profile的概念,不是很懂
//但不论如何,最终调用的是ActivityIntentResolver的queryIntent函数来得到实际的结果
..............
}
//Intent指定了Package名
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
return filterIfNotSystemUser(
//在指定Package的Activity中,进行匹配
mActivities.queryIntentForPackage(
intent, resolvedType, flags, pkg.activities, userId),
userId);
}
return new ArrayList<ResolveInfo>();
}
}
上面的代码明显的将Intent分为3类进行处理:
如果Intent指明了Component,则直接查询该Component对应的ActivityInfo,这也是我们常说的显示Intent;
如果Intent仅指明的Package名,则根据Package名找出对应的Package,然后再从中Package中找出匹配的ActivityInfo;
如果上述条件均不满足,也就是遇到常说的隐式Intent,则需要在全局系统中查找ActivityInfo。
这里我们重点分析一下,ActivityIntentResolver的queryIntent函数流程:
public List queryIntent(Intent intent, String resolvedType,
boolean defaultOnly, int userId) {
if (!sUserManager.exists(userId)) return null;
mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
//调用IntentResolver的queryIntent函数
return super.queryIntent(intent, resolvedType, defaultOnly, userId);
}
跟进IntentResolver的queryIntent函数:
public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
int userId) {
String scheme = intent.getScheme();
//用于保存最终结果
ArrayList finalList = new ArrayList();
...........
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.
if (resolvedType != null) {
//从PKMS中的mTypeToFilter、mWildTypeToFilter、mBaseTypeToFilter和mTypedActionToFilter中
//取出匹配类型的ActivityInfo填充到firstTypeCut、secondTypeCut和thirdTypeCut中
..................
}
// 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).
if (scheme != null) {
schemeCut = mSchemeToFilter.get(scheme);
..............
}
// 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.
if (resolvedType == null && scheme == null && intent.getAction() != null) {
firstTypeCut = mActionToFilter.get(intent.getAction());
..............
}
FastImmutableArraySet categories = getFastIntentCategories(intent);
//以下开始整合所有信息,进行一轮轮地筛选
//每次都将符合全部要求的ActivityInfo加入到finalList中
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);
}
sortResults(finalList);
............
return finalList;
}
上述代码的主要目的比较清晰,最终就是通过Intent携带的信息,从PKMS中取出完全与之要求相匹配的ActivityInfo。
按照逻辑来讲,queryIntent函数初始时,应该就是从不同的维度,将满足该维度的ActivityInfo存入对应的Cut数组中。
那么最终的结果,应该就是四个Cut数组的交集。
为了验证这一点,我们看一下buildResolveList函数:
//src依次为每个Cut数组,dest为finalList
private void buildResolveList(Intent intent, FastImmutableArraySet categories,
boolean debug, boolean defaultOnly,
String resolvedType, String scheme, F[] src, List dest, int userId) {
...........
for (i=0; inull; i++) {
............
// Do we already have this one?
//这里的allowFilterResult就相当于求交集的作用
//处理一个Cut数组时,若某个filter之前已经匹配过,那么就可以跳过
if (!allowFilterResult(filter, dest)) {
if (debug) {
Slog.v(TAG, " Filter's target already added");
}
continue;
}
//进行一次全条件匹配
match = filter.match(action, resolvedType, scheme, data, categories, TAG);
//匹配成功后,将ActivityInfo加入到dest中
...........
}
........
}
我们看看allowFilterResult函数,实际上该函数由IntentResolver的子类实现,此处需要跟进ActivityInfoResolver中的实现:
protected boolean allowFilterResult(
PackageParser.ActivityIntentInfo filter, List dest) {
ActivityInfo filterAi = filter.activity.info;
for (int i=dest.size()-1; i>=0; i--) {
ActivityInfo destAi = dest.get(i).activityInfo;
//匹配时返回false,将会跳过match检查
if (destAi.name == filterAi.name
&& destAi.packageName == filterAi.packageName) {
return false;
}
}
return true;
}
三、总结
从整个代码来看,PKMS中查找与指定Intent匹配的Activity的过程,主要的思想很清晰,但其中涉及的数据结构和细节还是相当琐碎的。
可以看出queryIntentActivities函数在处理隐式Intent时,主要通过IntentResolver来完成实际的查找工作。考虑到PKMS中四大组件的继承结构,我们大体可以推测出Provider和Service的Intent匹配过程,应该和Activity有许多相似之处。