Android动态部署二:APK安装及AndroidManifest.xml解析流程分析

转载请注明出处:http://blog.csdn.net/ximsfei/article/details/50886134

github地址:https://github.com/ximsfei/DynamicDeploymentApk
在上一篇文章:Android动态部署:Google原生Split APK浅析中,简单描述了Google实现SplitApk的机制。
接下来我们就开始一步步的实践,自己手动实现非安装apk的动态加载。
首先来了解一下APK解析安装的源码:
我们可以通过adb命令来安装apk到Android手机中:

1. adb push xxx /data/app
   此处/data/app第三方应用的安装目录,还可以为/system/app:系统应用,/vendor/app:方案商应用等。
2. adb install xxx
3. adb shell pm install yyy

其中adb push命令需要重启手机才能安装成功,xxx为宿主电脑中的apk路径,yyy则为Android手机中的路径

使用adb push 安装apk

源码解析:通过adb push命令将APK放到/data/app目录,手机重启后,会在PackageManagerService的构造方法中解析该apk,至于PackageManagerService这个类的构造方法何时调用,大家可以看看Android framework的启动源码,这里只需要知道,在Android启动后,SystemServer类中会调用PackageManagerService中的main方法。
SystemServer.java

private void startBootstrapServices() {
    ...
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
    ...
}

PackageManagerService.java

public static PackageManagerService main(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
    PackageManagerService m = new PackageManagerService(context, installer,
            factoryTest, onlyCore);
    ServiceManager.addService("package", m);
    return m;
}

调用ServiceManager的addService方法可能会引起PERMISSION DENIED问题:浅谈android add_service PERMISSION DENIED问题

下图为手机启动后PackageManagerService app解析的时序图,其中红色标注的为我们在研究过程需要关注的点:
Android动态部署二:APK安装及AndroidManifest.xml解析流程分析_第1张图片

  1. PackageParser.java中的new AssetManager & new Resources
    我们可以根据APK的绝对路径,及Host APK的Resources初始化一个插件APK的Resources对象
AssetManager assets = AssetManager.class.newInstance();
Reflect.create().setClass(AssetManager.class)
        .setMethod("addAssetPath", String.class).invoke(assets, apkPath);
Resources res = new Resources(assets, context.getResources().getDisplayMetrics(),
                    context.getResources().getConfiguration());

其中Reflect.java为自己封装的Java反射类,简单的说,就是通过反射调用AssetManager中的hide方法:addAssetPath

  1. PackageParser.java中的parseBaseApk & parseBaseApplication
    parseBaseApk方法会解析AndroidManifest.xml中包含的所有信息,最重要的部分为其调用的parseBaseApplication方法,在parseBaseApplication方法中会解析该APK所包含的四大组件的所有信息
private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
        String[] outError) throws XmlPullParserException, IOException {
    AttributeSet attrs = parser;
    ...

    final Package pkg = new Package(pkgName);
    TypedArray sa = res.obtainAttributes(attrs,
            com.android.internal.R.styleable.AndroidManifest);

    ...

    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
            && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {

        ...

        String tagName = parser.getName();
        if (tagName.equals("application")) {
            ...

            if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) {
                return null;
            }
        } else if (tagName.equals("overlay")) {
            ...
            XmlUtils.skipCurrentTag(parser);
        } else if (tagName.equals("key-sets")) {
            if (!parseKeySets(pkg, res, parser, attrs, outError)) {
                return null;
            }
        } else if (tagName.equals("permission-group")) {
            if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null) {
                return null;
            }
        } else if (tagName.equals("permission")) {
            if (parsePermission(pkg, res, parser, attrs, outError) == null) {
                return null;
            }
        } else if (tagName.equals("permission-tree")) {
            if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
                return null;
            }
        } else if (tagName.equals("uses-permission")) {
            if (!parseUsesPermission(pkg, res, parser, attrs)) {
                return null;
            }
        } else if (tagName.equals("uses-permission-sdk-m")
                || tagName.equals("uses-permission-sdk-23")) {
            if (!parseUsesPermission(pkg, res, parser, attrs)) {
                return null;
            }
        } 
        ...
    }
    ...

    return pkg;
}
private boolean parseBaseApplication(Package owner, Resources res,
        XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
    throws XmlPullParserException, IOException {
    final ApplicationInfo ai = owner.applicationInfo;
    final String pkgName = owner.applicationInfo.packageName;

    TypedArray sa = res.obtainAttributes(attrs,
            com.android.internal.R.styleable.AndroidManifestApplication);

    ...    //application标签解析

    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();
        if (tagName.equals("activity")) {
            Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
                    owner.baseHardwareAccelerated);
            if (a == null) {
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return false;
            }

            owner.activities.add(a);

        } else if (tagName.equals("receiver")) {
            Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
            if (a == null) {
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return false;
            }

            owner.receivers.add(a);

        } else if (tagName.equals("service")) {
            Service s = parseService(owner, res, parser, attrs, flags, outError);
            if (s == null) {
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return false;
            }

            owner.services.add(s);

        } else if (tagName.equals("provider")) {
            Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
            if (p == null) {
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return false;
            }

            owner.providers.add(p);

        } else if (tagName.equals("activity-alias")) {
            Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
            if (a == null) {
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return false;
            }

            owner.activities.add(a);

        }
        ...
    }

    ...
    return true;
}
  1. PackageManagerService.java中的scanPackageDirtyLI方法
    在该方法中,会扫描1,2中解析出来的信息,并且保存到本地变量中,方便后续调用queryIntent,resolveIntent等方法,检查该组件是否存在,并返回
// Currently known shared libraries.
final ArrayMap<String, SharedLibraryEntry> mSharedLibraries =
        new ArrayMap<String, SharedLibraryEntry>();
// 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();
......
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
        int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
    final File scanFile = new File(pkg.codePath);

    ...
    // writer
    synchronized (mPackages) {
        // Add the new setting to mSettings
        mSettings.insertPackageSettingLPw(pkgSetting, pkg);
        // Add the new setting to mPackages
        mPackages.put(pkg.applicationInfo.packageName, pkg);
        ...

        int N = pkg.providers.size();
        StringBuilder r = null;
        int i;
        for (i=0; i...
        }
        ...

        N = pkg.services.size();
        r = null;
        for (i=0; i...
        }
        ...

        N = pkg.receivers.size();
        r = null;
        for (i=0; i"receiver");
            ...
        }
        ...

        N = pkg.activities.size();
        r = null;
        for (i=0; i"activity");
            ...
        }
        ...

        N = pkg.permissionGroups.size();
        r = null;
        for (i=0; iif (cur == null) {
                mPermissionGroups.put(pg.info.name, pg);
                ...
            }
        }

        ...
    }

    return pkg;
}

使用adb install xxx & adb shell pm install yyy安装apk

下图为adb install安装APK的时序图,最终流程和使用adb push安装相同:
Android动态部署二:APK安装及AndroidManifest.xml解析流程分析_第2张图片

总结

这篇文章主要介绍了源码中APK安装以及AndroidManifest.xml解析的部分源码,在做Android 动态部署时,我们都会去自己解析AndroidManifest.xml文件,从中获取该插件APK中的信息,有哪些Activity,Receiver,Service,Provider等,我们自己写代码去解析固然好,不过我较为推荐的是porting源码中的PackageParser.java以及PackageManager.java, PackageManagerService.java中的部分代码,毕竟源码对AndroidManifest.xml的解析较为充分,不会有遗漏,而且porting过程中可以对源码有更深的理解,为我们后续的实现打好基础。

你可能感兴趣的:(Android,动态部署,Android插件化)