Android 9.0 PM机制系列(六)APK解析

前言

在本系列的前面文章中,我介绍了PackageInstaller的初始化和安装APK过程、PMS处理APK的安装和PMS的创建过程,这些文章中经常会涉及到一个类,那就是PackageParser,它用来在APK的安装过程中解析APK,那么APK是如何被解析的呢?这篇文章会给你答案。

1.引入PackageParser

Android世界中有很多包,比如应用程序的APK,Android运行环境的JAR包(比如framework.jar)和组成Android系统的各种动态库so等等,由于包的种类和数量繁多,就需要进行包管理,但是包管理需要在内存中进行,而这些包都是以静态文件的形式存在的,就需要一个工具类将这些包转换为内存中的数据结构,这个工具就是包解析器PackageParser。
Android 9.0 PM机制系列(三)PMS处理APK的安装这篇文章中,我们知道安装APK时需要调用PMS的installPackageLI方法:
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
   
    ...
    PackageParser pp = new PackageParser();//1
    pp.setSeparateProcesses(mSeparateProcesses);
    pp.setDisplayMetrics(mMetrics);
    pp.setCallback(mPackageParserCallback);
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
    final PackageParser.Package pkg;
    try {
   
        pkg = pp.parsePackage(tmpPackageFile, parseFlags);//2
    }
    ...
 }   

可以看到安装APK时,需要先在注释1处创建PackageParser,然后在注释2处调用PackageParser的parsePackage方法来解析APK。

2.PackageParser解析APK

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。

了解了APK,我们接着学习PackageParser解析APK,查看PackageParser的parsePackage方法:
frameworks/base/core/java/android/content/pm/PackageParser.java

public Package parsePackage(File packageFile, int flags) 
			throws PackageParserException {
   
    return parsePackage(packageFile, flags, false /* useCaches */);
}

public Package parsePackage(File packageFile, int flags, boolean useCaches)
           throws PackageParserException {
   
    Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
    if (parsed != null) {
   
        return parsed;
    }
    if (packageFile.isDirectory()) {
   //1
        parsed = parseClusterPackage(packageFile, flags);
    } else {
   
        parsed = parseMonolithicPackage(packageFile, flags);
    }
    cacheResult(packageFile, flags, parsed);

    return parsed;
}

注释1处,如果要解析的packageFile是一个目录,说明是Mutiple APK,就需要调用parseClusterPackage方法来解析,如果是Single APK则调用parseMonolithicPackage方法来解析。这里以复杂的parseClusterPackage方法为例,了解了这个方法,parseMonolithicPackage方法自然也看的懂。
在这里插入图片描述
frameworks/base/core/java/android/content/pm/PackageParser.java

private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
   
    final PackageLite lite = parseClusterPackageLite(packageDir, 0);//1
    if (mOnlyCoreApps && !lite.coreApp) {
   //2
        throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                "Not a coreApp: " + packageDir);
    }

    // Build the split dependency tree.
    SparseArray<int[]> splitDependencies = null;
    final SplitAssetLoader assetLoader;
    if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
   
        try {
   
            splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
            assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
        } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
   
            throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
        }
    } else {
   
        assetLoader = new DefaultSplitAssetLoader(lite, flags);
    }

    try {
   
        final AssetManager assets = assetLoader.getBaseAssetManager();
        final File baseApk = new File(lite.baseCodePath);
        final Package pkg = parseBaseApk(baseApk, assets, flags);//3
        if (pkg == null) {
   
            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                    "Failed to parse base APK: " + baseApk);
        }

        if (!ArrayUtils.isEmpty(lite.splitNames)) {
   
            final int num = lite.splitNames.length;//4
            pkg.splitNames = lite.splitNames;
            pkg.splitCodePaths = lite.splitCodePaths;
            pkg.splitRevisionCodes = lite.splitRevisionCodes;
            pkg.splitFlags = new int[num];
            pkg.splitPrivateFlags = new int[num];
            pkg.applicationInfo.splitNames = pkg.splitNames;
            pkg.applicationInfo.splitDependencies = splitDependencies;
            pkg.applicationInfo.splitClassLoaderNames = new String[num];

            for (int i = 0; i < num; i++) {
   
                final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
                parseSplitApk(pkg, i, splitAssets, flags);//5
            }
        }

        pkg.setCodePath(packageDir.getCanonicalPath());
        pkg.setUse32bitAbi(lite.use32bitAbi);
        return pkg;
    } catch (IOException e) {
   
        throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                "Failed to get path: " + lite.baseCodePath, e);
    } finally {
   
        IoUtils.closeQuietly(assetLoader);
    }
}
  1. 注释1处调用parseClusterPackageLite方法用于轻量级解析目录文件,之所以要轻量级解析是因为解析APK是一个复杂耗时的操作,这里的逻辑并不需要APK所有的信息。parseClusterPackageLite方法内部会通过parseApkLite方法解析每个Mutiple APK,得到每个Mutiple APK对应的ApkLite(轻量级APK信息),然后再将这些ApkLite封装为一个PackageLite(轻量级包信息)并返回。
  2. 注释2处,mOnlyCoreApps用来指示PackageParser是否只解析“核心”应用,“核心”应用指的是AndroidManifest中属性coreApp值为true,只解析“核心”应用是为了创建一个极简的启动环境。mOnlyCoreApps在创建PMS时就一路传递过来,如果我们加密了设备,mOnlyCoreApps值就为true。另外可以通过PackageParser的setOnlyCoreApps方法来设置mOnlyCoreApps的值。
    lite.coreApp表示当前包是否包含“核心”应用,如果不满足注释2的条件就会抛出异常。
  3. 注释3处的parseBaseApk方法用于解析base APK,注释4处获取split APK的数量,根据这个数量在注释5处遍历调用parseSplitApk来解析每个split APK。这里主要查看parseBaseApk方法,如下所示。

frameworks/base/core/java/android/content/pm/PackageParser.java

private static final String MNT_EXPAND = "/mnt/expand/";

private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
            throws PackageParserException {
   
    final String apkPath = apkFile.getAbsolutePath();

    String volumeUuid = null;
    if (apkPath.startsWith(MNT_EXPAND)) {
   
        final int end = apkPath.indexOf('/', MNT_EXPAND.length());
        volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);//1
    }

    mParseError = PackageManager.INSTALL_SUCCEEDED;
    mArchiveSourcePath = apkFile.getAbsolutePath();

    XmlResourceParser parser = null;
    try {
   
        final int cookie = assets.findCookieForPath(apkPath);
        if (cookie == 0) {
   
            throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                    "Failed adding asset path: " + apkPath);
        }
        parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
        final Resources res = new Resources(assets, mMetrics, null);

        final String[] outError = new String[1];
       
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值