Android动态部署二:APK安装及AndroidManifest.xml解析流程分析
Posted ximsfei
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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解析的时序图,其中红色标注的为我们在研究过程需要关注的点:
- 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; i++)
PackageParser.Provider p = pkg.providers.get(i);
p.info.processName = fixProcessName(pkg.applicationInfo.processName,
p.info.processName, pkg.applicationInfo.uid);
mProviders.addProvider(p);
...
...
N = pkg.services.size();
r = null;
for (i=0; i<N; i++)
PackageParser.Service s = pkg.services.get(i);
s.info.processName = fixProcessName(pkg.applicationInfo.processName,
s.info.processName, pkg.applicationInfo.uid);
mServices.addService(s);
...
...
N = pkg.receivers.size();
r = null;
for (i=0; i<N; i++)
PackageParser.Activity a = pkg.receivers.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
mReceivers.addActivity(a, "receiver");
...
...
N = pkg.activities.size();
r = null;
for (i=0; i<N; i++)
PackageParser.Activity a = pkg.activities.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
mActivities.addActivity(a, "activity");
...
...
N = pkg.permissionGroups.size();
r = null;
for (i=0; i<N; i++)
PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i);
PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name);
if (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动态部署二:APK安装及AndroidManifest.xml解析流程分析的主要内容,如果未能解决你的问题,请参考以下文章
Android动态部署五:如何从插件apk中启动Service
Android动态部署五:如何从插件apk中启动Service
Android动态部署三:如何从插件apk中启动Activity(-)