该系列文章总纲链接:专题分纲目录 Android Framework 包管理子系统
本章关键点总结 & 说明:
导图是不断迭代的,这里主要关注➕ 查询intent组件部分。主要对PkgMS查询组件的流程进行分析,通过intent找到对应的组件并返回。简介了intent和intentFilter,说明了查询的接口,同时对queryIntentActivities接口进行流程解读。
1 intent 和 intentFilter简介
1.1 intent简介
Intent中文是“意图”的意思,它是Android系统中很常用的概念;个人认为 这种基本思想源于对生活的抽象;人与人沟通用的是语言,表达的是意图,比如:
startActivity(Intent intent)
startService(Intent intent)
bindService(Intent intent)
sendBrodcast(Intent intent)
之所以可以用使用intent表达意图,是因为只要用特定的某种格式就可以精确的表达“意图”,因此格式的设定非常关键,intent的属性分类如下:
- 启动类:有ComponentName(显式),Action(隐式),Category(隐式)。
- 传值类:有Data(隐式),Type(隐式),Extra(隐式、显式)。
- 启动模式类:有Flag,这意味着可以根据flag做不同的处理。
我们对比我们平时的沟通模式,启动类 对应 动词,传值类 对应 宾语,加起来就是一个动宾结构,而这就是语言中最核心的结构,“主谓宾” 是我们表达意图最常用、也是最熟悉的方式。那么根据是否有主语决定了 是显示调用还是隐式调用。而这也是intent分类的根源所在(以上仅为自己的知识积累所做的一个概述),intent的分类如下:
Explicit Intents(显示):这类Intent明确指明 主语。在代码中通过setComponent或setClass来锁定目标对象。处理这种Intent高效
Implicit Intents(隐示):这一类Intents只指明动宾关系,主语是一个范围。对于这类意图,处理相对复杂。
1.2 intentFilter简介
在与人沟通的模型中,找合适的人(与谁沟通)是一个很关键的事情,比如找对象、找合作伙伴、筛选简历等等,首先我们会给出筛选的原则,之后会根据筛选原则去和遇到的每个人去匹配,而在Android中这项工作被称为Intent Resolution。在做匹配工作时,将以Intent Filter 列出的3项内容为参考 标准,具体步骤如下:
- 匹配IntentFilter的Action,如果Intent设置的Action不满足IntentFilter的Action,则匹配失败。如果IntentFilter未设定Action,则匹配成功。
- 检查IntentFilter的Category,匹配方法同Action的匹配,唯一有些例外的是Category为CATEGORY_DEFAULT的情况。
- 检查Data。Data的匹配过程比较繁琐,因为它和IntentFilter设置的Data内容有关
这里IntentFilter中的Data可以包括两个内容:
- URI:完整格式为“scheme://host:port/path”,包含4个部分,scheme、host、port和path。其中host和port合起来标示URI authority,指明服务器网络地址(IP & port)。由于URI最多可包含4个部分,因此要根据情况相应部分做匹配检查。
- Date type:指定数据的MIME类型(注意:URI中也可以携带数据的类型信息,所以在匹配过程中,还需要考虑URI中指定的数据类型)
2 通过intent查询组件
2.1 查询基础
PkgMS中很重的一项工作就是根据intent来查询处理Intent的续组件信息,处理Intent的查询接口如下:
queryIntentActivities
queryIntentServices
queryIntentReceivers
queryIntentContentProviders
系统中响应某个intent的组件可能有多个,因此返回值是一个列表,android系统中用ResolveInfo类来表示所有组件,ResolveInfo定义如下:
public class ResolveInfo implements Parcelable {
private static final String TAG = "ResolveInfo";
public ActivityInfo activityInfo;
public ServiceInfo serviceInfo;
public ProviderInfo providerInfo;
public IntentFilter filter;
//...
}
2.2 queryIntentActivities案例
几种查询组件的方式类似,这里以queryIntentActivities为例来分析查询过程,代码如下:
@Override
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
//检查调用接口的用户权限
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "query intent activities");
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);
//通过模块信息得到ActivityInfo
final ActivityInfo ai = getActivityInfo(comp, flags, userId);
if (ai != null) {
final ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
return list;
}
// reader
synchronized (mPackages) {
final String pkgName = intent.getPackage();
if (pkgName == null) {//如果intent中没有包名,则在系统中查找
List<CrossProfileIntentFilter> matchingFilters =
getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
// Check for results that need to skip the current profile.
ResolveInfo resolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent,
resolvedType, flags, userId);
if (resolveInfo != null) {
List<ResolveInfo> result = new ArrayList<ResolveInfo>(1);
result.add(resolveInfo);
return result;
}
//在当前profile中查找
resolveInfo = queryCrossProfileIntents(
matchingFilters, intent, resolvedType, flags, userId);
// Check for results in the current profile.
List<ResolveInfo> result = mActivities.queryIntent(
intent, resolvedType, flags, userId);
if (resolveInfo != null) {
result.add(resolveInfo);
Collections.sort(result, mResolvePrioritySorter);
}
return result;
}
//如果intent中有包名,则在指定的包中查找。
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
return mActivities.queryIntentForPackage(intent, resolvedType, flags,
pkg.activities, userId);
}
return new ArrayList<ResolveInfo>();
}
}
该方法会根据Intent的信息来分别处理。整体来说,分三步处理如下:
- 如果intent中指明Component,则直接查询该Component对应的ActivityInfo,执行getActivityInfo后直接返回ActivityInfo。这是最快的方式。
- 如果只有包名,则调用queryIntentForPackage来,根据Package名找到该Package,然后再从该Package包含的Activities中进行匹配查询。
- 如果前面条件都不满足,则调用QueryIntent来搜索所有的安装包,也是最慢的查询方式。
queryIntentActivities的函数实现,目的就是进行Intent匹配查询。