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 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解析的时序图,其中红色标注的为我们在研究过程需要关注的点:
- 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
- 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;}
- 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安装相同:
总结
这篇文章主要介绍了源码中APK安装以及AndroidManifest.xml解析的部分源码,在做Android 动态部署时,我们都会去自己解析AndroidManifest.xml文件,从中获取该插件APK中的信息,有哪些Activity,Receiver,Service,Provider等,我们自己写代码去解析固然好,不过我较为推荐的是porting源码中的PackageParser.java以及PackageManager.java, PackageManagerService.java中的部分代码,毕竟源码对AndroidManifest.xml的解析较为充分,不会有遗漏,而且porting过程中可以对源码有更深的理解,为我们后续的实现打好基础。
更多相关文章
- 如何导入android sdk 的 sample中的源码
- Android RetainFragment状态保存的方法
- Android Volley框架使用方法详解
- H5页面调用android方法传json格式
- Android提交数据到服务的四种方法!!!
- Volley 源码解析
- 不支持gif动态图片
- Android-string.xml动态替换文本
- 【android】Cursor记录集游标、ListView和SimpleCursorAdapter、