转载请注明出处: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 xxx3. 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过程中可以对源码有更深的理解,为我们后续的实现打好基础。

更多相关文章

  1. 如何导入android sdk 的 sample中的源码
  2. Android RetainFragment状态保存的方法
  3. Android Volley框架使用方法详解
  4. H5页面调用android方法传json格式
  5. Android提交数据到服务的四种方法!!!
  6. Volley 源码解析
  7. 不支持gif动态图片
  8. Android-string.xml动态替换文本
  9. 【android】Cursor记录集游标、ListView和SimpleCursorAdapter、

随机推荐

  1. android下拉菜单三级联动
  2. View常见XML属性及相关方法
  3. Android伸手党系列之二:Android开发基础知
  4. Android 底层学习札记
  5. Android 带图标的textview
  6. Android 多屏适配
  7. Android(安卓)-> 如何避免Handler引起内
  8. Android 融云SDK 集成
  9. 【边做项目边学Android】手机安全卫士05_
  10. Android(安卓)Launcher 之 图标加框 优化