Android 解析APK包

  它实现在ParsingPackageUtils类的静态方法parseDefault(ParseInput input, File file, @ParseFlags int parseFlags, @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions, boolean collectCertificates)中,看一下它的代码:

    @NonNull
    public static ParseResult<ParsingPackage> parseDefault(ParseInput input, File file,
            @ParseFlags int parseFlags,
            @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
            boolean collectCertificates) {
    
    
        ParseResult<ParsingPackage> result;

        ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, splitPermissions,
                new Callback() {
    
    
                    @Override
                    public boolean hasFeature(String feature) {
    
    
                        // Assume the device doesn't support anything. This will affect permission
                        // parsing and will force <uses-permission/> declarations to include all
                        // requiredNotFeature permissions and exclude all requiredFeature
                        // permissions. This mirrors the old behavior.
                        return false;
                    }

                    @Override
                    public ParsingPackage startParsingPackage(
                            @NonNull String packageName,
                            @NonNull String baseApkPath,
                            @NonNull String path,
                            @NonNull TypedArray manifestArray, boolean isCoreApp) {
    
    
                        return new ParsingPackageImpl(packageName, baseApkPath, path,
                                manifestArray);
                    }
                });
        try {
    
    
            result = parser.parsePackage(input, file, parseFlags);
            if (result.isError()) {
    
    
                return result;
            }
        } catch (PackageParser.PackageParserException e) {
    
    
            return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Error parsing package", e);
        }

        try {
    
    
            ParsingPackage pkg = result.getResult();
            if (collectCertificates) {
    
    
                pkg.setSigningDetails(
                        ParsingPackageUtils.getSigningDetails(pkg, false /* skipVerify */));
            }

            // Need to call this to finish the parsing stage
            pkg.hideAsParsed();

            return input.success(pkg);
        } catch (PackageParser.PackageParserException e) {
    
    
            return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Error collecting package certificates", e);
        }
    }

   1、新建 ParsingPackageUtils 实例 在这里设置的回调Callback的startParsingPackage() 方法,它是用来生成parseDefault()实际返回的对象类型。
   2、调用新生成的ParsingPackageUtils 实例的parsePackage()得到ParseResult类型result。
   3、根据参数 collectCertificates 来判断是否执行签名验证。可以参考Android APK文件的签名V2查找、验证Android APK文件完整性验证
  其中比较主要的是第二步,解析包得到包信息。

解析包

  相关代码如下:

    public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
            int flags)
            throws PackageParserException {
    
    
        if (packageFile.isDirectory()) {
    
    
            return parseClusterPackage(input, packageFile, flags);
        } else {
    
    
            return parseMonolithicPackage(input, packageFile, flags);
        }
    }

  根据参数packageFile 是目录还是文件分别执行 parseClusterPackage(input, packageFile, flags) 还是 parseMonolithicPackage(input, packageFile, flags)。
  像用户手动安装应用的时候,就是文件形式的。在开机时安装应用,则是目录的形式。

文件形式解析

    private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
            int flags) throws PackageParserException {
    
    
        final ParseResult<PackageLite> liteResult =
                ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags);
        if (liteResult.isError()) {
    
    
            return input.error(liteResult);
        }

        final PackageLite lite = liteResult.getResult();
        if (mOnlyCoreApps && !lite.isCoreApp()) {
    
    
            return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
                    "Not a coreApp: " + apkFile);
        }

        final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
        try {
    
    
            final ParseResult<ParsingPackage> result = parseBaseApk(input,
                    apkFile,
                    apkFile.getCanonicalPath(),
                    assetLoader, flags);
            if (result.isError()) {
    
    
                return input.error(result);
            }

            return input.success(result.getResult()
                    .setUse32BitAbi(lite.isUse32bitAbi()));
        } catch (IOException e) {
    
    
            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Failed to get path: " + apkFile, e);
        } finally {
    
    
            IoUtils.closeQuietly(assetLoader);
        }
    }

  首先通过ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags)得到ParseResult类型liteResult。
  其次生成一个DefaultSplitAssetLoader类对象。
  通过parseBaseApk()得到ParseResult类对象result。
  这里提到的lite.isUse32bitAbi(),该值是Manifest配置文件中标签 application 下的 use32bitAbi 属性值,如果不配置 默认为false。

解析得到apk文件的轻量级详情包

  它实现在ApkLiteParseUtils类的parseMonolithicPackageLite()方法中:

    /**
     * Parse lightweight details about a single APK files.
     */
    public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input,
            File packageFile, int flags) {
    
    
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
        try {
    
    
            final ParseResult<ApkLite> result = parseApkLite(input, packageFile, flags);
            if (result.isError()) {
    
    
                return input.error(result);
            }

            final ApkLite baseApk = result.getResult();
            final String packagePath = packageFile.getAbsolutePath();
            return input.success(
                    new PackageLite(packagePath, baseApk.getPath(), baseApk, null,
                            null, null, null, null, null, baseApk.getTargetSdkVersion()));
        } finally {
    
    
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

  可见这里主要是调用parseApkLite(input, packageFile, flags)得到ApkLite对象,然后将它封装到PackageLite对象中,并且返回。其中,PackageLite的构造函数中,使用了packagePath和baseApk.getPath()。packagePath是文件的绝对路径。baseApk.getPath()是什么呢,在这里也是文件的绝对路径。这俩值是赋值给了PackageLite的成员变量mPath和mBaseApkPath。看其注释,说它俩可能是不同的,因为移动或者重命名APK文件。
  parseApkLite(input, packageFile, flags)主要接着调用parseApkLiteInner(ParseInput input, File apkFile, FileDescriptor fd, String debugPathName, int flags),它再调用parseApkLite(ParseInput input, String codePath, XmlResourceParser parser, PackageParser.SigningDetails signingDetails),主要解析的就是"AndroidManifest.xml"文件,但是它不会解析四大组件相关的属性信息。所以说它是轻量级。主要的解析信息都在parseApkLite(ParseInput input, String codePath, XmlResourceParser parser, PackageParser.SigningDetails signingDetails)中,看一下最后生成的ApkLite对象:

        return input.success(
                new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
                        configForSplit, usesSplitName, isSplitRequired, versionCode,
                        versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails,
                        coreApp, debuggable, profilableByShell, multiArch, use32bitAbi,
                        useEmbeddedDex, extractNativeLibs, isolatedSplits, targetPackage,
                        overlayIsStatic, overlayPriority, minSdkVersion, targetSdkVersion,
                        rollbackDataPolicy));

  这里面的值都是从"AndroidManifest.xml"文件中取得的,后面用到再说。

解析得到apk文件的解析包

  它主要实现在parseBaseApk(ParseInput input, File apkFile, String codePath, SplitAssetLoader assetLoader, int flags),

    private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
            String codePath, SplitAssetLoader assetLoader, 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);
        }

        if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);

        final AssetManager assets = assetLoader.getBaseAssetManager();
        final int cookie = assets.findCookieForPath(apkPath);
        if (cookie == 0) {
    
    
            return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                    "Failed adding asset path: " + apkPath);
        }

        try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
                ANDROID_MANIFEST_FILENAME)) {
    
    
            final Resources res = new Resources(assets, mDisplayMetrics, null);

            ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
                    parser, flags);
            if (result.isError()) {
    
    
                return input.error(result.getErrorCode(),
                        apkPath + " (at " + parser.getPositionDescription() + "): "
                                + result.getErrorMessage());
            }

            final ParsingPackage pkg = result.getResult();
            if (assets.containsAllocatedTable()) {
    
    
                final ParseResult<?> deferResult = input.deferError(
                        "Targeting R+ (version " + Build.VERSION_CODES.R + " and above) requires"
                                + " the resources.arsc of installed APKs to be stored uncompressed"
                                + " and aligned on a 4-byte boundary",
                        DeferredError.RESOURCES_ARSC_COMPRESSED);
                if (deferResult.isError()) {
    
    
                    return input.error(INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED,
                            deferResult.getErrorMessage());
                }
            }

            ApkAssets apkAssets = assetLoader.getBaseApkAssets();
            boolean definesOverlayable = false;
            try {
    
    
                definesOverlayable = apkAssets.definesOverlayable();
            } catch (IOException ignored) {
    
    
                // Will fail if there's no packages in the ApkAssets, which can be treated as false
            }

            if (definesOverlayable) {
    
    
                SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
                int size = packageNames.size();
                for (int index = 0; index < size; index++) {
    
    
                    String packageName = packageNames.valueAt(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 & 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);
        }
    }

  首先得到解析包的volumeUuid,当然这得是它的安装路径是以"/mnt/expand/“开头。然后通过截取”/mnt/expand/“和它的下一个”/“之间的值作为volumeUuid。
  assetLoader在这里是DefaultSplitAssetLoader对象,它是可以用来加载splits APK的,所以它可能对应有多个APK文件。这里通过文件路径apkPath找到AssetManager中对应的APK的位置。然后再通过cookie找到对应APK文件的"AndroidManifest.xml”,最后它的解析信息在XmlResourceParser类型的parser对象中。
  其实解析APK文件也是挺复杂的,它的内容在AssetManager的getBaseAssetManager()中。它涉及到C++层的实现。这里只是简单描述一下相应过程,说通逻辑。
  接着就是调用另一重载函数parseBaseApk(),来生成ParsingPackage对象。看一下它的实现:

    private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
            String codePath, Resources res, XmlResourceParser parser, int flags)
            throws XmlPullParserException, IOException {
    
    
        final String splitName;
        final String pkgName;

        ParseResult<Pair<String, String>> packageSplitResult =
                ApkLiteParseUtils.parsePackageSplitNames(input, parser);
        if (packageSplitResult.isError()) {
    
    
            return input.error(packageSplitResult);
        }

        Pair<String, String> packageSplit = packageSplitResult.getResult();
        pkgName = packageSplit.first;
        splitName = packageSplit.second;

        if (!TextUtils.isEmpty(splitName)) {
    
    
            return input.error(
                    PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
                    "Expected base APK, but found split " + splitName
            );
        }

        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);
            final ParseResult<ParsingPackage> result =
                    parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
            if (result.isError()) {
    
    
                return result;
            }

            return input.success(pkg);
        } finally {
    
    
            manifestArray.recycle();
        }
    }

  ApkLiteParseUtils.parsePackageSplitNames(input, parser)是为了解析"package"、"split"属性,解析完之后,将对应值放在Pair里。
  接下里会调用回调mCallback.startParsingPackage()方法,我们返回到ParsingPackageUtils的定义处。可知,它的实际类型是ParsingPackageImpl对象。
  接着调用parseBaseApkTags(input, pkg, manifestArray, res, parser, flags)来得到具体的解析包对象。

    private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
            TypedArray sa, Resources res, XmlResourceParser parser, int flags)
            throws XmlPullParserException, IOException {
    
    
        ParseResult<ParsingPackage> sharedUserResult = parseSharedUser(input, pkg, sa);
        if (sharedUserResult.isError()) {
    
    
            return sharedUserResult;
        }

        pkg.setInstallLocation(anInteger(PARSE_DEFAULT_INSTALL_LOCATION,
                R.styleable.AndroidManifest_installLocation, sa))
                .setTargetSandboxVersion(anInteger(PARSE_DEFAULT_TARGET_SANDBOX,
                        R.styleable.AndroidManifest_targetSandboxVersion, sa))
                /* Set the global "on SD card" flag */
                .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0);

        boolean foundApp = 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;
            }

            String tagName = parser.getName();
            final ParseResult result;

            // <application> has special logic, so it's handled outside the general method
            if (TAG_APPLICATION.equals(tagName)) {
    
    
                if (foundApp) {
    
    
                    if (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 {
    
    
                result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
            }

            if (result.isError()) {
    
    
                return input.error(result);
            }
        }

        if (!foundApp && ArrayUtils.size(pkg.getInstrumentations()) == 0) {
    
    
            ParseResult<?> deferResult = input.deferError(
                    "<manifest> does not contain an <application> or <instrumentation>",
                    DeferredError.MISSING_APP_TAG);
            if (deferResult.isError()) {
    
    
                return input.error(deferResult);
            }
        }
		…………
        return input.success(pkg);
    }

  可以看到这里主要是manifest文件中"application"标签和同级其他标签下的属性和标签。"application"标签下的主要是调用parseBaseApplication(input, pkg, res, parser, flags),和"application"标签同级的使用parseBaseApkTag(tagName, input, pkg, res, parser, flags)解析。
  在这里parseBaseApplication(input, pkg, res, parser, flags)就会将四大组件的信息都解析出来。
  这样执行完毕,就将解析包信息都放在ParsingPackageImpl对象中。

目录形式解析

  所谓以目录形式解析,就是给了一个文件是目录,安装文件包括在它包括的文件中。
  看下它的实现:

    private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
            int flags) {
    
    
        final ParseResult<PackageLite> liteResult =
                ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, 0);
        if (liteResult.isError()) {
    
    
            return input.error(liteResult);
        }

        final PackageLite lite = liteResult.getResult();
        if (mOnlyCoreApps && !lite.isCoreApp()) {
    
    
            return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
                    "Not a coreApp: " + packageDir);
        }

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

        try {
    
    
            final File baseApk = new File(lite.getBaseApkPath());
            final ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
                    lite.getPath(), assetLoader, flags);
            if (result.isError()) {
    
    
                return input.error(result);
            }

            ParsingPackage pkg = result.getResult();
            if (!ArrayUtils.isEmpty(lite.getSplitNames())) {
    
    
                pkg.asSplit(
                        lite.getSplitNames(),
                        lite.getSplitApkPaths(),
                        lite.getSplitRevisionCodes(),
                        splitDependencies
                );
                final int num = lite.getSplitNames().length;

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

            pkg.setUse32BitAbi(lite.isUse32bitAbi());
            return input.success(pkg);
        } catch (PackageParserException e) {
    
    
            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Failed to load assets: " + lite.getBaseApkPath(), e);
        } finally {
    
    
            IoUtils.closeQuietly(assetLoader);
        }
    }

  先通过ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, 0)得到PackageLite对象。
  接着创建SplitAssetLoader对象。lite.isIsolatedSplits()来自基础APK的配置文件里的"isolatedSplits"属性值,默认为false。如果它设置为true,并且存在splite apk,则会构造SplitAssetDependencyLoader。它会构造split apk间的依赖关系,如果存在依赖,会在加载apk(这里主要是加载ID资源表)的时候,一起加载。
  再通过parseBaseApk(input, baseApk, lite.getPath(), assetLoader, flags)得到ParsingPackage对象。
  如果是split apk,会接着调用parseSplitApk(input, pkg, i, splitAssets, flags)。

得到APK目录相关的轻量级详情

  看下ApkLiteParseUtils类的parseClusterPackageLite()方法:

    /**
     * Parse lightweight details about a directory of APKs.
     */
    public static ParseResult<PackageLite> parseClusterPackageLite(ParseInput input,
            File packageDir, int flags) {
    
    
        final File[] files = packageDir.listFiles();
        if (ArrayUtils.isEmpty(files)) {
    
    
            return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
                    "No packages found in split");
        }
        // Apk directory is directly nested under the current directory
        if (files.length == 1 && files[0].isDirectory()) {
    
    
            return parseClusterPackageLite(input, files[0], flags);
        }

        String packageName = null;
        int versionCode = 0;

        final ArrayMap<String, ApkLite> apks = new ArrayMap<>();
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
        try {
    
    
            for (File file : files) {
    
    
                if (isApkFile(file)) {
    
    
                    final ParseResult<ApkLite> result = parseApkLite(input, file, flags);
                    if (result.isError()) {
    
    
                        return input.error(result);
                    }

                    final ApkLite lite = result.getResult();
                    // Assert that all package names and version codes are
                    // consistent with the first one we encounter.
                    if (packageName == null) {
    
    
                        packageName = lite.getPackageName();
                        versionCode = lite.getVersionCode();
                    } else {
    
    
                        if (!packageName.equals(lite.getPackageName())) {
    
    
                            return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
                                    "Inconsistent package " + lite.getPackageName() + " in " + file
                                            + "; expected " + packageName);
                        }
                        if (versionCode != lite.getVersionCode()) {
    
    
                            return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
                                    "Inconsistent version " + lite.getVersionCode() + " in " + file
                                            + "; expected " + versionCode);
                        }
                    }

                    // Assert that each split is defined only oncuses-static-libe
                    if (apks.put(lite.getSplitName(), lite) != null) {
    
    
                        return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
                                "Split name " + lite.getSplitName()
                                        + " defined more than once; most recent was " + file);
                    }
                }
            }
        } finally {
    
    
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }

        final ApkLite baseApk = apks.remove(null);
        return composePackageLiteFromApks(input, packageDir, baseApk, apks);
    }

  如果当前目录下面只有一个目录,会递归调用parseClusterPackageLite(ParseInput input, File packageDir, int flags)方法,并且会将这个目录作为参数传递。
  如果目录下有多个以".apk"结尾的文件,则它为split apk安装形式。首先通过parseApkLite(input, file, flags)解析得到ApkLite对象,这个前面讲过。接下来,可以看到如果是split apk安装形式,基本apk是不用配置"split"属性值,其他apk则是需要配置不同的"split"属性值。并且会将"split"属性值和ApkLite对象放入ArrayMap<String, ApkLite> apks中,接着从apks中取出null key对应的ApkLite对象作为基本对象。
  接下来会调用composePackageLiteFromApks(input, packageDir, baseApk, apks),最后它会调用composePackageLiteFromApks( ParseInput input, File packageDir, ApkLite baseApk, ArrayMap<String, ApkLite> splitApks, boolean apkRenamed)方法来生成PackageLite对象,看下它的实现:

    public static ParseResult<PackageLite> composePackageLiteFromApks(
            ParseInput input, File packageDir, ApkLite baseApk,
            ArrayMap<String, ApkLite> splitApks, boolean apkRenamed) {
    
    
        if (baseApk == null) {
    
    
            return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
                    "Missing base APK in " + packageDir);
        }
        // Always apply deterministic ordering based on splitName
        final int size = ArrayUtils.size(splitApks);

        String[] splitNames = null;
        boolean[] isFeatureSplits = null;
        String[] usesSplitNames = null;
        String[] configForSplits = null;
        String[] splitCodePaths = null;
        int[] splitRevisionCodes = null;
        if (size > 0) {
    
    
            splitNames = new String[size];
            isFeatureSplits = new boolean[size];
            usesSplitNames = new String[size];
            configForSplits = new String[size];
            splitCodePaths = new String[size];
            splitRevisionCodes = new int[size];

            splitNames = splitApks.keySet().toArray(splitNames);
            Arrays.sort(splitNames, sSplitNameComparator);

            for (int i = 0; i < size; i++) {
    
    
                final ApkLite apk = splitApks.get(splitNames[i]);
                usesSplitNames[i] = apk.getUsesSplitName();
                isFeatureSplits[i] = apk.isFeatureSplit();
                configForSplits[i] = apk.getConfigForSplit();
                splitCodePaths[i] = apkRenamed ? new File(packageDir,
                        splitNameToFileName(apk)).getAbsolutePath() : apk.getPath();
                splitRevisionCodes[i] = apk.getRevisionCode();
            }
        }

        final String codePath = packageDir.getAbsolutePath();
        final String baseCodePath = apkRenamed ? new File(packageDir,
                splitNameToFileName(baseApk)).getAbsolutePath() : baseApk.getPath();
        return input.success(
                new PackageLite(codePath, baseCodePath, baseApk, splitNames, isFeatureSplits,
                        usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes,
                        baseApk.getTargetSdkVersion()));
    }

  这里主要是处理split apk的多个文件的情况,这里注意的一点splitCodePath的名字会根据参数apkRenamed 来赋不同的值,如果apkRenamed 为true,则值为目录 + “split_” + splitName + “.apk”;如果为false,则为文件的路径名。
  同样在为baseCodePath 赋值时,apkRenamed 为true,则值为目录 + “base.apk”;如果为false,则为文件的路径名。
  在这里codePath 和 baseCodePath 的值是不同的,codePath为目录名,baseCodePath 为基本apk文件路径。它俩值分别对应PackageLite对象的成员mPath、mBaseApkPath。
  这样就生成了PackageLite对象。

解析得到解析包对象

  实现在parseBaseApk(ParseInput input, File apkFile, String codePath, SplitAssetLoader assetLoader, int flags)中,该方法在前面说过,在这里不同的是,codePath是目录,不是安装文件。

解析包ParsingPackageImpl对象

  从前面知道,最终得到的解析包是ParsingPackageImpl对象,它里面包含所有解析出来的信息。看一下它的初始化函数:

    @VisibleForTesting
    public ParsingPackageImpl(@NonNull String packageName, @NonNull String baseApkPath,
            @NonNull String path, @Nullable TypedArray manifestArray) {
    
    
        this.packageName = TextUtils.safeIntern(packageName);
        this.mBaseApkPath = baseApkPath;
        this.mPath = path;

        if (manifestArray != null) {
    
    
            versionCode = manifestArray.getInteger(R.styleable.AndroidManifest_versionCode, 0);
            versionCodeMajor = manifestArray.getInteger(
                    R.styleable.AndroidManifest_versionCodeMajor, 0);
            setBaseRevisionCode(
                    manifestArray.getInteger(R.styleable.AndroidManifest_revisionCode, 0));
            setVersionName(manifestArray.getNonConfigurationString(
                    R.styleable.AndroidManifest_versionName, 0));

            setCompileSdkVersion(manifestArray.getInteger(
                    R.styleable.AndroidManifest_compileSdkVersion, 0));
            setCompileSdkVersionCodename(manifestArray.getNonConfigurationString(
                    R.styleable.AndroidManifest_compileSdkVersionCodename, 0));

            setIsolatedSplitLoading(manifestArray.getBoolean(
                    R.styleable.AndroidManifest_isolatedSplits, false));

        }
    }

  在这里,我们知道mBaseApkPath 和mPath 的区别了,在用目录形式的解析时,mPath 是包含apk文件的目录。它里面包含特别多的成员变量和方法。在这里,我们知道它是从以上哪些函数中得到的,等以后使用到,就去这些函数中查找。

总结

  解析apk包信息时,一种是通过apk文件作为参数解析,另外一种是通过apk文件所在目录作为参数来解析。
  解析出来的类对象是ParsingPackageImpl类型的,它主要解析的是"AndroidManifest.xml"文件,类对象的成员也主要对应配置文件的属性和值。

猜你喜欢

转载自blog.csdn.net/q1165328963/article/details/133690581