一、前言
前段时间在看当下主流的安卓插件化技术原理的时候,发现目前插件化技术对于四大组件的处理基本都是通过代理来实现的(动态注册的广播接收器除外)。简单说就是需要先在主包里预埋一个注册在AndroidManifest.xml中的组件,再由这个组件来连接SystemServer和插件中的业务逻辑。
这里难免就会有一些疑问,系统从AndroidManifest.xml文件解析出来的四大组件的信息存放在哪里?能否不利用预埋+代理的技术,直接通过修改中间的存储容器来实现四大组件的插件化?
要想解答这些疑惑,我们就得知道系统是如何从AndroidManifest.xml中解析出四大组件的?以及解析后的组件信息又是如何保存的?
系统中负责解析AndroidManifest.xml文件的系统服务是PackageManagerService(以下简称PMS)。PMS中关于AndroidManifest.xml文件的处理逻辑入口主要有两处,一个是SystemServer进程启动时,另一个则是应用安装/更新时。两个入口对于xml的解析流程大体上是一致的,本篇文章只分析SystemServer进程启动时,PMS对AndroidManifest.xml的解析流程。
二、相关知识
2.1 方法后缀LI、LIF、LPr、LPw的含义
PMS中有很多方法的名字后缀都是LI/LIF/LPr/LPw,因此我们需要先简单了解下这些后缀的含义是什么。这些后缀涉及到PMS两个很重要的锁mPackages
和mInstallLock
以及一个mFrozenPackages
变量。相关后缀的含义见下表。详见:https://blog.csdn.net/u013553529/article/details/61962439。
三、调用流程
SystemServer的启动属于开机流程中的一步,入口如下:
- SystemServer.main()
/**
* The main entry point from zygote.
* zygote进程会调用该方法
*/
public static void main(String[] args) {
new SystemServer().run();
}
main()方法中new了一个SystemServer对象,并调用了它的run()方法
- SystemServer.run()
private void run() {
try {
// 省略部分代码,只列出关键部分的代码
// The system server should never make non-oneway calls
Binder.setWarnOnBlocking(true);
// Initialize native services.
System.loadLibrary("android_servers");
// Create the system service manager.
// SystemServiceManager是整个Service的大管家
mSystemServiceManager = new SystemServiceManager(mSystemContext);
mSystemServiceManager.setStartInfo(mRuntimeRestart,
mRuntimeStartElapsedTime, mRuntimeStartUptime);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
} finally {
t.traceEnd(); // InitBeforeStartServices
}
// Start services.
try {
t.traceBegin("StartServices");
// start critical services, include PMS/AMS
startBootstrapServices(t);
// start essential services
startCoreServices(t);
// start other service
startOtherServices(t);
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
t.traceEnd(); // StartServices
}
}
run()方法中主要做了一些系统启动时的初始化工作,其中很关键的一步就是启动各项service服务。PMS就是在startBootstrapServices()中初始化的。
- SystemServer.startBootstrapServices(@NonNull TimingsTraceAndSlog t)
/**
* Starts the small tangle of critical services that are needed to get the system off the
* ground. These services have complex mutual dependencies which is why we initialize them all
* in one place here. Unless your service is also entwined in these dependencies, it should be
* initialized in one of the other functions.
*/
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
// 省略部分代码
t.traceBegin("StartPackageManagerService");
try {
Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
} finally {
Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");
}
// Now that the package manager has started, register the dex load reporter to capture any
// dex files loaded by system server.
// These dex files will be optimized by the BackgroundDexOptService.
SystemServerDexLoadReporter.configureSystemServerDexReporter(mPackageManagerService);
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
// 省略部分代码
}
startBootstrapServices()方法中调用了PMS的main()方法来初始化PMS服务
- PackageManagerService.main()
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// 省略部分代码
// new一个Java层的PMS对象
PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest);
// 省略部分代码
// 将new出来的PMS对象注册到ServiceManager中,之后我们便能够通过Context对象的getPackageManager()来获取PM对象了,这部分源码在Context.getPackageManager()/ContextImpl.getPackageManager()中
m.installWhitelistedSystemPackages();
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
这里我们主要关注PMS的实例化过程,PMS的构造函数如下:
- PackageManagerService.PackageManagerService()
/** Directory where installed applications are stored */
private static final File sAppInstallDir =
new File(Environment.getDataDirectory(), "app");
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
// 省略部分代码
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
packageParser, executorService);
}
// 省略部分代码
}
PMS的构造函数中通过scanDirTracedLI()来扫描已安装的三方App apk信息,路径为/data/app/
- PackageManagerService.scanDirTracedLI()
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
- PackageManagerService.scanDirLI()
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
PackageParser2 packageParser, ExecutorService executorService) {
ParallelPackageParser parallelPackageParser =
new ParallelPackageParser(packageParser, executorService);
// Submit files for parsing in parallel
int fileCount = 0;
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
// 继续往下调用了parallelPackageParser.submit()
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
}
- ParallelPackageParser.submit(File scanFile, int parseFlags)
/**
* Submits the file for parsing
* @param scanFile file to scan
* @param parseFlags parse flags
*/
public void submit(File scanFile, int parseFlags) {
mExecutorService.submit(() -> {
ParseResult pr = new ParseResult();
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
try {
pr.scanFile = scanFile;
// 继续往下调用了parserPackage()
pr.parsedPackage = parsePackage(scanFile, parseFlags);
} catch (Throwable e) {
pr.throwable = e;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
try {
mQueue.put(pr);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
// Propagate result to callers of take().
// This is helpful to prevent main thread from getting stuck waiting on
// ParallelPackageParser to finish in case of interruption
mInterruptedInThread = Thread.currentThread().getName();
}
});
}
- ParallelPackageParser.parsePackage(File scanFile, int parseFlags)
protected ParsedPackage parsePackage(File scanFile, int parseFlags)
throws PackageParser.PackageParserException {
return mPackageParser.parsePackage(scanFile, parseFlags, true);
}
- PackageParser2.parsePackage(File packageFile, int flags, boolean useCaches)
/**
* TODO(b/135203078): Document new package parsing
*/
@AnyThread
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
// 有缓存的话优先使用缓存
if (useCaches && mCacher != null) {
ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags);
if (parsed != null) {
return parsed;
}
}
long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
ParseInput input = mSharedResult.get().reset();
// 继续往下调用了parsePackage()
ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
// 省略部分代码
return parsed;
}
- ParsingPackageUtils.parsePackage(ParseInput input, File packageFile, int flags)
public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
int flags)
throws PackageParserException {
if (packageFile.isDirectory()) {
// 如果给定的路径是文件夹,则循环解析其中的所有apk文件
return parseClusterPackage(input, packageFile, flags);
} else {
// 如果给定的路径是文件,则调用parseMonolithicPackage()解析文件
return parseMonolithicPackage(input, packageFile, flags);
}
}
注: Android5.0引入了Split APK机制,这是为了解决65536上限以及APK安装包越来越大等问题。Split APK机制可以将一个APK,拆分成多个独立APK。
在引入了Split APK机制后,APK有两种分类:Single APK:安装文件为一个完整的APK,即base APK。Android称其为Monolithic。
Mutiple APK:安装文件在一个文件目录中,其内部有多个被拆分的APK,这些APK由一个 base APK和一个或多个split APK组成。Android称其为Cluster。
我们直接看解析Single APK文件的逻辑
- ParsingPackageUtils.parseMonolithicPackage(ParseInput input, File apkFile, int flags)
/**
* Parse the given APK file, treating it as as a single monolithic package.
* <p>
* Note that this <em>does not</em> perform signature verification; that
* must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
*/
private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
int flags) throws PackageParserException {
// 省略部分代码
final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
try {
// 真正开始解析apk信息
ParseResult<ParsingPackage> result = parseBaseApk(input,
apkFile,
apkFile.getCanonicalPath(),
assetLoader.getBaseAssetManager(), flags);
if (result.isError()) {
return input.error(result);
}
return input.success(result.getResult()
.setUse32BitAbi(lite.use32bitAbi));
} catch (IOException e) {
return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to get path: " + apkFile, e);
} finally {
IoUtils.closeQuietly(assetLoader);
}
}
- parsingPackageUtils.parseBaseApk(ParseInput input, File apkFile, String codePath, AssetManager assets, int flags)
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
String codePath, AssetManager assets, int flags) {
final String apkPath = apkFile.getAbsolutePath();
// 省略部分代码
// 打开apk的AndroidManifest.xml文件
try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
PackageParser.ANDROID_MANIFEST_FILENAME)) {
final Resources res = new Resources(assets, mDisplayMetrics, null);
// 开始解析AndroidManifest.xml的信息
ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
parser, flags);
// 省略部分代码
ApkAssets apkAssets = assets.getApkAssets()[0];
if (apkAssets.definesOverlayable()) {
SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
int size = packageNames.size();
for (int index = 0; index < size; index++) {
String packageName = packageNames.get(index);
Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
for (String overlayable : overlayableToActor.keySet()) {
pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
}
}
}
}
pkg.setVolumeUuid(volumeUuid);
if ((flags & PackageParser.PARSE_COLLECT_CERTIFICATES) != 0) {
pkg.setSigningDetails(getSigningDetails(pkg, false));
} else {
pkg.setSigningDetails(SigningDetails.UNKNOWN);
}
return input.success(pkg);
} catch (Exception e) {
return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to read manifest from " + apkPath, e);
}
}
- parsingPackageUtils.parseBaseApk(ParseInput input, String apkPath, String codePath, Resources res, XmlResourceParser parser, int flags)
/**
* Parse the manifest of a <em>base APK</em>. When adding new features you
* need to consider whether they should be supported by split APKs and child
* packages.
*
* @param apkPath The package apk file path
* @param res The resources from which to resolve values
* @param parser The manifest parser
* @param flags Flags how to parse
* @return Parsed package or null on error.
*/
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
String codePath, Resources res, XmlResourceParser parser, int flags)
throws XmlPullParserException, IOException, PackageParserException {
// 省略部分代码
final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
try {
final boolean isCoreApp =
parser.getAttributeBooleanValue(null, "coreApp", false);
final ParsingPackage pkg = mCallback.startParsingPackage(
pkgName, apkPath, codePath, manifestArray, isCoreApp);
// 继续往下调用了parseBaseApkTags()
final ParseResult<ParsingPackage> result =
parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
if (result.isError()) {
return result;
}
return input.success(pkg);
} finally {
manifestArray.recycle();
}
}
- parsingPackageUtils.parseBaseApkTags(ParseInput input, ParsingPackage pkg, TypedArray sa, Resources res, XmlResourceParser parser, int flags)
private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
TypedArray sa, Resources res, XmlResourceParser parser, int flags)
throws XmlPullParserException, IOException {
//省略部分代码
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > depth)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
String tagName = parser.getName();
final ParseResult result;
if (PackageParser.TAG_APPLICATION.equals(tagName)) {
// 找到<applicaton>标签,四大组件的声明都在里面
if (foundApp) {
if (PackageParser.RIGID_PARSER) {
result = input.error("<manifest> has more than one <application>");
} else {
Slog.w(TAG, "<manifest> has more than one <application>");
result = input.success(null);
}
} else {
foundApp = true;
// 关键在这里
result = parseBaseApplication(input, pkg, res, parser, flags);
}
} else {
// 解析除<application>外的其他标签
result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
}
if (result.isError()) {
return input.error(result);
}
}
// 省略部分代码
return input.success(pkg);
}
- parsingPackageUtils.parseBaseApplication(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
throws XmlPullParserException, IOException {
// 省略部分代码
boolean hasActivityOrder = false;
boolean hasReceiverOrder = false;
boolean hasServiceOrder = false;
final int depth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > depth)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final ParseResult result;
String tagName = parser.getName();
boolean isActivity = false;
switch (tagName) {
case "activity":
isActivity = true;
// fall-through
case "receiver":
ParseResult<ParsedActivity> activityResult =
ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
res, parser, flags, PackageParser.sUseRoundIcon, input);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
if (isActivity) {
hasActivityOrder |= (activity.getOrder() != 0);
pkg.addActivity(activity);
} else {
hasReceiverOrder |= (activity.getOrder() != 0);
pkg.addReceiver(activity);
}
}
result = activityResult;
break;
case "service":
ParseResult<ParsedService> serviceResult =
ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
flags, PackageParser.sUseRoundIcon, input);
if (serviceResult.isSuccess()) {
ParsedService service = serviceResult.getResult();
hasServiceOrder |= (service.getOrder() != 0);
pkg.addService(service);
}
result = serviceResult;
break;
case "provider":
// Provider的解析
ParseResult<ParsedProvider> providerResult =
ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
flags, PackageParser.sUseRoundIcon, input);
if (providerResult.isSuccess()) {
// 注册provider信息
pkg.addProvider(providerResult.getResult());
}
result = providerResult;
break;
case "activity-alias":
activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
parser, PackageParser.sUseRoundIcon, input);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
hasActivityOrder |= (activity.getOrder() != 0);
pkg.addActivity(activity);
}
result = activityResult;
break;
default:
result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
break;
}
if (result.isError()) {
return input.error(result);
}
}
if (TextUtils.isEmpty(pkg.getStaticSharedLibName())) {
// Add a hidden app detail activity to normal apps which forwards user to App Details
// page.
// 如果App没有入口Activity,就将其入口设置为App详情页
ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg);
if (a.isError()) {
return input.error(a);
}
pkg.addActivity(a.getResult());
}
return input.success(pkg);
}
parseBaseApplication()方法中PMS根据tag来区分四大组件,并调用相应的方法做进一步的解析。最终PMS会通过ParsingPackage的addXXX()方法将解析到的组件信息添加到对应的List中。ParsingPackage是一个接口,对应的实现类为ParsingPackageImpl
- ParsingPackageImpl.addXXX()
@NonNull
protected List<ParsedActivity> activities = emptyList();
@NonNull
protected List<ParsedActivity> receivers = emptyList();
@NonNull
protected List<ParsedService> services = emptyList();
@NonNull
protected List<ParsedProvider> providers = emptyList();
@Override
public ParsingPackageImpl addActivity(ParsedActivity parsedActivity) {
this.activities = CollectionUtils.add(this.activities, parsedActivity);
addMimeGroupsFromComponent(parsedActivity);
return this;
}
@Override
public ParsingPackageImpl addReceiver(ParsedActivity parsedReceiver) {
this.receivers = CollectionUtils.add(this.receivers, parsedReceiver);
addMimeGroupsFromComponent(parsedReceiver);
return this;
}
@Override
public ParsingPackageImpl addService(ParsedService parsedService) {
this.services = CollectionUtils.add(this.services, parsedService);
addMimeGroupsFromComponent(parsedService);
return this;
}
@Override
public ParsingPackageImpl addProvider(ParsedProvider parsedProvider) {
this.providers = CollectionUtils.add(this.providers, parsedProvider);
addMimeGroupsFromComponent(parsedProvider);
return this;
}
至此,四大组件的信息就都被解析并且保存下来了。
三、小结
整个流程看下来我们会发现,PMS对四大组件信息的处理采用的是实时解析AndroidManifest.xml文件的方式,中间并没有多余的转存逻辑。因此,想要通过修改中间容器的方式来实现四大组件的插件化看起来是不太可行的。只能老老实实走预埋+代理的逻辑。
但是,实时解析的方式虽然安全准确,但是速度慢呀。当你手机里安装的App越来越多,设备的开机速度就会变得越来越慢。因此,新版本上安卓系统为了提升设备的启动速度,在PMS解析AndroidManifest.xml文件的流程中引入了相应的缓存逻辑来优化解析速度。