前言
在Android
开发中,Intent
是极其重要的一个类,它是各个组件、进程之间通讯的纽带。那么系统是如何通过Intent
来查找到对应的组件呢?比如Activity
跳转,用户设置好了一个Intent
之后 ,系统如何查找与匹配符合要求的Activity
? 这就是本篇需要学习的内容。
App信息表的创建
在文章Android源码中单例模式 中,我们知道系统启动之后就会注册各种系统服务,如WindowManagerService
、AcitivityManagerService
等,其中有一个就是PackageManagerService
(后续简称PMS)。PMS
启动之后,会扫描系统中已经安装的apk
目录,例如系统App
安装的目录为/system/app
,第三方应用的目录为/data/app
,PMS
会解析apk
包下的AndroidMainfest.xml
文件得到App的相关信息。AndroidMainfest.xml
又包含了Activity
、Service
等组件的注册信息 ,当PMS
扫描并且解析完成这些信息之后就会构建好了整个apk
的信息树,大致流程如图:
PMS解析已安装的apk
PMS
是一个尽职尽责的类, 对于apk的解析工作它在构造函数时,就已经开始了!我们看下PMS
的部分构造函数代码:
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
...
//获取data目录
File dataDir = Environment.getDataDirectory();
// /data/app 目录也就是第三方app安装的目录
mAppInstallDir = new File(dataDir, "app");
...
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
...
// Find base frameworks (resource packages without code).
//加载frameworks资源
scanDirTracedLI(frameworkDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);
// Collect ordinary system packages.
//获取系统app安装路径
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
//扫描系统app安装路径
scanDirTracedLI(systemAppDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
...
//扫描第三方app安装路径
scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
...
//扫描已经安装完的apk包
scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null);
从上述代码中可以看到,PMS
不仅需要加载系统已经安装的apk
,在此之前还有加载Framework资源,加载了Framework资源之后才开始对扫描的指定目录的apk文件进行解析,上述代码只给出了系统apk的安装目录和第三方应用安装目录,扫描函数为scanDirTracedLI
我们看看这里面的实现:
private void scanDirTracedLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
scanDirLI(dir, parseFlags, scanFlags, currentTime);
}
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
//获取该目录下的所有文件
final File[] files = dir.listFiles();
...
// Submit files for parsing in parallel
//解析目录下所有的apk文件
int fileCount = 0;
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
//如果不是apk文件 忽略
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
//解析apk文件
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
}
public static final boolean isApkFile(File file) {
return isApkPath(file.getName());
}
//是否已.apk结尾
public static boolean isApkPath(String path) {
return path.endsWith(".apk");
}
scanDirLI
就是扫描指定目录下的所有apk
文件,然后通过调用submit
进行解析,重点应该在submit
方法中,我们继续往下看:
/**
* 提交文件解析
* Submits the file for parsing
* @param scanFile file to scan
* @param parseFlags parse falgs
*/
public void submit(File scanFile, int parseFlags) {
mService.submit(() -> {
//解析结果
ParseResult pr = new ParseResult();
try {
//创建一个包解析器
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCacheDir(mCacheDir);
pp.setCallback(mPackageParserCallback);
pr.scanFile = scanFile;
//解析apk包
pr.pkg = parsePackage(pp, scanFile, parseFlags);
} catch (Throwable e) {
pr.throwable = e;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
//将解析结果放入队列
mQueue.put(pr);
});
}
//调用PackageParser中的parsePackage方法解析
@VisibleForTesting
protected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,
int parseFlags) throws PackageParser.PackageParserException {
return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
}
在scanDirLI
方法中首先构造了一个PackageParser,
也就是一个apk
解析器,然后调用PackageParser
中的parsePackage
方法解析,具体代码:
public Package parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
if (packageFile.isDirectory()) {
//是文件夹类型 解析整个文件夹
parsed = parseClusterPackage(packageFile, flags);
} else {
//解析单个apk
parsed = parseMonolithicPackage(packageFile, flags);
}
//缓存结果
cacheResult(packageFile, flags, parsed);
return parsed;
}
在parsePackage
方法中会根据packageFile
的类型来选择不同的解析方法,我们直接看解析单个apk
的parseMonolithicPackage
方法;
@Deprecated
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
...
//构造应用资源
final AssetManager assets = newConfiguredAssetManager();
try {
//会调用parseBaseApk(apkPath, res, parser, flags, outError)
final Package pkg = parseBaseApk(apkFile, assets, flags);
pkg.setCodePath(apkFile.getAbsolutePath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
在parseMonolithicPackage
方法中我们看到会调用有三个参数的parseBaseApk(apkFile, assets, flags);
这三个参数的parseBaseApk
会调用五个参数的parseBaseApk
,我们先看下三个参数的parseBaseApk
:
//三个参数的parseBaseApk
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {
//获取apk绝对路径
final String apkPath = apkFile.getAbsolutePath();
...
Resources res = null;
XmlResourceParser parser = null;
try {
//获取资源
res = new Resources(assets, mMetrics, null);
//获取解析ANDROID_MANIFEST_FILENAME清单文件的解析器
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final String[] outError = new String[1];
//调用五个参数的parseBaseApk解析清单文件
final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
...
}
}
//五个参数的parseBaseApk
private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
...
//创建一个包实例
final Package pkg = new Package(pkgName);
//获取清单文件中属性
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
//获取清单文件中版本号
pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
//获取清单文件中版本名
pkg.mVersionName = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_versionName, 0);
//回收
sa.recycle();
//真正解析清单文件
return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}
解析清单文件
上面代码主要是获取清单文件的信息,真正解析清单文件的各个节点的方法是parseBaseApkCommon
,我们看下如何实现的:
private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
IOException {
...
//解析AndroidMainfest中的元素
int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
//解析application标签 四大组件都在这里解析
if (tagName.equals(TAG_APPLICATION)) {
...
if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
return null;
}
//解析权限标签
} else if (tagName.equals(TAG_PERMISSION)) {
if (!parsePermission(pkg, res, parser, outError)) {
return null;
}
}
return pkg;
}
这个parseBaseApkCommon
方法才是真正解析AndroidMainfest.xml
的方法,这里给出了两个标签,即Application
和permission
。Application
中包含Activity、``Service
等标签,也就是Intent
所需要的标签。我们看下Application
标签的parseBaseApplication
方法:
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
//应用信息
final ApplicationInfo ai = owner.applicationInfo;
//包名
final String pkgName = owner.applicationInfo.packageName;
//获取Application标签下的TypedArray
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestApplication);
//解析applicatinon的属性 包括name lable icon logo roundIcon
if (!parsePackageItemInfo(owner, ai, outError,
"<application>", sa, false /*nameRequired*/,
com.android.internal.R.styleable.AndroidManifestApplication_name,
com.android.internal.R.styleable.AndroidManifestApplication_label,
com.android.internal.R.styleable.AndroidManifestApplication_icon,
com.android.internal.R.styleable.AndroidManifestApplication_roundIcon,
com.android.internal.R.styleable.AndroidManifestApplication_logo,
com.android.internal.R.styleable.AndroidManifestApplication_banner)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
...
//解析Application标签下的Activity等标签
final int innerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
//获取标签名
String tagName = parser.getName();
//解析Activity
if (tagName.equals("activity")) {
Activity a = parseActivity(owner, res, parser, flags, outError, false,
owner.baseHardwareAccelerated);
owner.activities.add(a);
//解析receiver
} else if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
owner.receivers.add(a);
//解析service
} else if (tagName.equals("service")) {
Service s = parseService(owner, res, parser, flags, outError);
owner.services.add(s);
//解析provider
} else if (tagName.equals("provider")) {
Provider p = parseProvider(owner, res, parser, flags, outError);
owner.providers.add(p);
//解析meta-data
} else if (parser.getName().equals("meta-data")) {
// note: application meta-data is stored off to the side, so it can
// remain null in the primary copy (we like to avoid extra copies because
// it can be large)
if ((owner.mAppMetaData = parseMetaData(res, parser, owner.mAppMetaData,
outError)) == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
}
return true;
}
看到Activity
、Service
、Provider
、Received
等标签时这个过程我们有了深刻的了解,从parseBaseApplication
中我们看到这个过程就是普通的xml
解析,根据不同的标签调用不同的解析方法,例如,解析Activity
会调用parseActivity
方法,然后返回一个Activity
的实例, 并将这个实例添加到Package对象的activitys
的列表中。
PackageManagerService的scanPackageTracedLI
此时,我们需要回到上述构造方法scanDirTracedLI
解析完成之后的scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null);
方法,该函数的具体实现如下:
private PackageParser.Package scanPackageTracedLI(PackageParser.Package pkg,
final int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user) {
final PackageParser.Package scannedPkg;
// 调用scanPackageLI进行扫描apk包
scannedPkg = scanPackageLI(pkg, policyFlags, scanFlags, currentTime, user);
return scannedPkg;
}
//scanPackageLI方法
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, final int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user){
...
//调用scanPackageDirtyLI进行扫描apk包
final PackageParser.Package res = scanPackageDirtyLI(pkg, policyFlags, scanFlags,
currentTime, user);
return res;
}
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
final int policyFlags, final int scanFlags, long currentTime, @Nullable UserHandle user){
...
//提交包信息设置
commitPackageSettings(pkg, pkgSetting, user, scanFlags,
(policyFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
...
return pkg;
}
以上函数依次调用,最后scanPackageDirtyLI
函数会调用commitPackageSettings
对所解析完成的包提交信息,我们看下具体实现:
*/
private void commitPackageSettings(PackageParser.Package pkg, PackageSetting pkgSetting,
UserHandle user, int scanFlags, boolean chatty) throws PackageManagerException {
...
synchronized (mPackages) {
int N = pkg.providers.size();
int i;
for (i=0; i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
p.info.processName = fixProcessName(pkg.applicationInfo.processName,
p.info.processName);
mProviders.addProvider(p);
...
}
}
N = pkg.services.size();
for (i=0; i<N; i++) {
PackageParser.Service s = pkg.services.get(i);
s.info.processName = fixProcessName(pkg.applicationInfo.processName,
s.info.processName);
mServices.addService(s);
}
N = pkg.receivers.size();
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.receivers.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName);
mReceivers.addActivity(a, "receiver");
}
N = pkg.activities.size();
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.activities.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName);
mActivities.addActivity(a, "activity");
}
}
}
我们看到,这里将上一步解析的Activity、
Service添加到mActivities
、mServices
中,这些类型定义是PMS
的字段,我们看看下面的程序。
public class PackageManagerService extends IPackageManager.Stub
implements PackageSender {
...
// All available activities, for your resolving pleasure.
final ActivityIntentResolver mActivities =
new ActivityIntentResolver();
// All available receivers, for your resolving pleasure.
final ActivityIntentResolver mReceivers =
new ActivityIntentResolver();
// All available services, for your resolving pleasure.
final ServiceIntentResolver mServices = new ServiceIntentResolver();
// All available providers, for your resolving pleasure.
final ProviderIntentResolver mProviders = new ProviderIntentResolver();
...
}
到这一步,整个已安装的apk
的信息树就建立起来了,每个apk
的应用名、包名、图标、Activity
、Service
等信息都存储在系统中,当用户使用Intent
跳转到某个Activity
或者启动某个Service
时,系统则会到这个信息表中进行查找,符合要求的组建就会被启动。这样就通过Intent
将整个系统的组件连接在一起,使得Android
系统成为一个组件可复用、灵活的系统。
Intent的精确匹配
上面分析了apk
信息表的构建过程,下面我们分析一下Intent
的查找与匹配的过程。在开发中,我们需要启动每个具体的Activity
,代码大致是这样的:
Intent intent = new Intent(this, MyActivity.class);
startActivity(intent);
这种情况下指定了具体的组件,也就是MyActivty,此时在系统查找组件时会使用精确匹配,我们称为显示Intent
,还有一种情况是不指定具体的组件,而是给出一些模糊的查询属性,例如:
Intent intent = new Intent(Intent.ACTION_SEND);
startActivity(intent);
这类我们称为隐式Intent
。
下面我们一起看看这些Intent的查找与匹配过程,startActivity这个函数经过几个函数的转发,最终会调用startActivityForResult
,我们看看这个函数。
*/
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
Instrumentation.ActivityResult ar =
//启动Activity
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
...
//发送启动请求
mMainThread.sendActivityResult(mToken, mEmbeddedID, requestCode, ar.getResultCode(),ar.getResultData());
}
Activity
中的startActivityForResult
函数直接调用了Instrumentation
的execStartActivity
方法,具体代码如下:
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
...
try {
//将Intent中的数据迁移到粘贴板中
intent.migrateExtraStreamToClipData();
//准备离开当前进程
intent.prepareToLeaveProcess(who);
//调用AMS的startActivity
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
}
return null;
}
execStartActivity
里面其实就是调用ActivityManagerService
的startActivity
方法,这个方法里面调用了
ActivityStarter
对象的startActivityMayWait
方法,这个方法里面调用了ActivityStackSupervisor
的resolveIntent
方法,这个方法里面最后调用了PMS
的resolveIntent
方法,PMS
又出现在了我们的视野中,在resolveIntent
方法中就调用了自身的queryIntentActivitiesInternal
方法,这个方法返回一个ResolveInfo的列表,
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, int userId) {
//获取Intent的ComponentName对象
ComponentName comp = intent.getComponent();
//精确跳转时这个对象不为空
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
//通过Intent直接获取ActivityInfo对象
final ActivityInfo ai = getActivityInfo(comp, flags, userId);
if (ai != null) {
final ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
return list;
}
...
//Component为空则为隐式Intent
synchronized (mPackages) {
...
if (pkgName == null) {
//包名为空就模糊匹配
result = filterIfNotSystemUser(mActivities.queryIntent(
intent, resolvedType, flags, userId), userId);
...
}else{
//通过包名获取到Package对象
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
result = filterIfNotSystemUser(
//通过获取Package对象获取到ResoloveInfo
mActivities.queryIntentForPackage(intent, resolvedType, flags, pkg.activities, userId),userId);
...
}
}
return result;
}
上述函数大致过程:如果Intent
指明了Comonet
,那么直接通过Componet
就可以找到ActivityInfo
列表,这个列表的数量只有一个,这个ActivityInfo
就是指定的那个组件,如果没有指定具体的组件,那么Component
为空,此时先看Intent
是否指定了要调转到的目标组件所在的包名,如果没有包名,调用queryIntent
模糊匹配,例如Action、Category等,如果有包名,就会调用queryIntentForPackage
通过包名获取到对应的ActivityInfo。这里需要注意mActivities
是就是上一节说到的存储了从AndroidMainfest.xml
中解析到的Activity
既然已经找到了对应的Activity
的信息,那么最好就是启动对应的Activity
。对于显示Intent
来说,就是直接跳转到具体的Activity
中,对于隐式Intent
来说,可能会弹出系统的列表框让用户选择相应的应用。
总结
在系统启动时PackageManagerService
会启动,此时PMS
将解析所有已安装的应用的信息,构建一个信息表,当用户通过Intent
来跳转到某个组件时,会根据Intent
中包含的信息到PMS
中查找对应的组件列表,最后跳转到目标组件中。
参考
《Android源码设计模式》