Android系统APP安装流程
更多内容,欢迎关注公众号:tmac_lover
这篇文章介绍一下Android里安装一个apk文件的完整流程,我们以pm install安装一个新的app为例介绍。
1. pm命令
当我们使用
pm install -r /sdcard/test.apk
这样的pm命令来安装app的时候, 最终调用的是Pm.java的runInstall()方法
private int runInstall() { ... ... mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags, installerPackageName, verificationParams, abi, userId); ... ...}
最后通过binder调用PackageManagerService.java的installPackageAsUser()方法,然后就开始真正的进行apk的安装工作。可以看到,pm install命令进行app安装的时候,
是不会经过PackageInstaller的。
2. PackageManagerService
installPackageAsUser()方法里实现apk的安装主要可以分三个阶段。我们先看下总体流程图:
2.1 准备阶段
在进行app安装之前,需要先做一些准备工作。
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer, int installFlags, String installerPackageName, VerificationParams verificationParams, String packageAbiOverride, int userId) { ... ... final Message msg = mHandler.obtainMessage(INIT_COPY); msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName, null, verificationParams, user, packageAbiOverride, null); mHandler.sendMessage(msg);}
这里先构造了一个InstallParams对象,这里存放一些app文件安装需要的信息,比如apk文件的路径,安装时的flags, 以及安装完成之后的回调等等。然后通过Handler发送一条INIT_COPY消息。
class PackageHandler extends Handler { void doHandleMessage(Message msg) { switch (msg.what) { case INIT_COPY: { if (!mBound) { // 第一次安装app, 会连接DefaultContainerService if (!connectToService()) { params.serviceError(); return; } else { // 然后将app安装请求放到mPendingInstalls里 mPendingInstalls.add(idx, params); } } else { // 如果不是第一次安装app, DefaultContainerService已经是连接状态, // 直接将app安装请求放到mPendingInstalls里 mPendingInstalls.add(idx, params); if (idx == 0) { mHandler.sendEmptyMessage(MCS_BOUND); } } break; } case MCS_BOUND: { // 不管是哪种情况,INIT_COPY之后,一定会再发一个MSC_BOUND的消息 // 这里开始调用InstallParams类的startCopy()方法 if (params.startCopy()) { if (mPendingInstalls.size() > 0) { mPendingInstalls.remove(0); } // 如果mPendingInstalls里还有等待安装的app, 依次执行 if (mPendingInstalls.size() == 0) { if (mBound) { removeMessages(MCS_UNBIND); Message ubmsg = obtainMessage(MCS_UNBIND); sendMessageDelayed(ubmsg, 10000); } } else { mHandler.sendEmptyMessage(MCS_BOUND); } } } } }}
总结一下,在安装app之前的准备工作主要有:
1. 构造一个InstallParams对象,这个对象里包含了apk文件的信息和一些安装相关的参数
2. 发送INIT_COPY消息,这时如果没有连接DefaultContainerService, 先连接这个service;还有就是将apk安装请求放入mPendingInstalls里
3. 再发送MCS_BOUND消息,调用InstallParams对象的startCopy()方法开始安装app;如果有多个app等待安装,循环发MSC_BOUND消息,执行安装操作
2.2 handleStartCopy
前面的准备工作最后,调用InstallParams对象的startCopy()方法开始app的安装工作,startCopy()里分为两部分,handleStartCopy()和handleReturnCode()。先看下handleStartCopy()流程图:
- 图中第2步获取app安装的基本信息;先通过parsePackageLite()提取apk包中的信息,然后依据AndroidManifest.xml里的”android:installLocation”属性和apk安装需要的大小以及目标分区的剩余空间大小来决定app最终是安装在data分区,还是sdcard上。
- 第6步installLocationPolicy()方法对第2步返回的apk信息做进一步确认。如果系统中已经安装过此app, 需要确认新安装的app版本号是否比之前的高;同时还会根据上一步获取的安装位置信息设置app安装需要的Flag
- 第7步copyApk()方法完成将需要安装的apk文件拷贝到要安装的分区的一个零时文件夹里,并重命名为base.apk,比如/data/app/vmdl1309479077.tmp/base.apk下。同时还会将.apk包里的.so库拷贝到/data/app/vmdl1309479077.tmp/libs下
所以handleStartCopy()主要的工作就是将要安装的apk文件和使用的.so库拷贝到要安装的分区的零时目录下
2.3 handleReturnCode
handleReturnCode里会完成app安装的剩余动作,并发送安装成功的广播。
- 第4步installPackageLI()方法里完成了安装app的核心工作,后面专门分析。
- 第8步通过Handler发送POST_INSTALL消息,这个消息的处理函数如下:
class PackageHandler extends Handler { void doHandleMessage(Message msg) { switch (msg.what) { ... ... case POST_INSTALL: { ... ... // 发送ACTION_PACKAGE_ADDED这条广播 sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, null, null, firstUsers); if (args.observer != null) { // 如果有回调的话,调用回调方法, 通知app安装完成 args.observer.onPackageInstalled(res.name, res.returnCode, res.returnMsg, extras); } ... ... break; } } }}
在收到POST_INSTALL消息后,主要完成两条操作:
- 发送ACTION_PACKAGE_ADDED广播
- 如果在调用installPackageAsUser()方法时有传入IPackageInstallObserver2作为回调,则调用回调方法报告安装状态
接下来看下第4步installPackageLI()方法:
- 第2步parsePackage()通过解析apk包和apk包里的AndroidManifest.xml,解析出所有apk的信息,放到Package对象中。
- 第3步collectCertificates()方法对apk文件进行签收校验,确保apk文件没有被更改过以及签名正确。第4步通过collectManifestDigest()方法获取AndroidManifest.xml的摘要值。
- 第5步derivePackageAbi()方法通过apk包里使用的so库确定app进程的abi,关于abi,可以查看之前的一篇文章:
- 第6步performDexOpt()通过socket和installd通信,将apk包里的classes.dex转化为虚拟机可以直接运行的文件格式,关于dex优化,可以查看之前的一篇文章:
- 第7步doRename()方法将前面handleStartCopy()里的零时文件夹/data/app/vmdl1309479077更改成/data/app/$(package_name),这个新的文件夹将是app安装后的最终目录
- 第8步installNewPackageLI()将完成一个新的app的安装,如果是安装之前已经存在的apk, 则这里调用的是replacePackageLI()
接下来看看installNewPackageLI()方法的流程:
- verifySignaturesLP()使用上一步中collectCertificates()里获取的签名信息进行验证,主要分两步:一是如果是安装一个系统中已经存在的同名app, 则和已经存在的app签名进行对比,保证两者签名相同;二是如果系统中已经存在和正在安装的app有相同UID的app,
则需要保证安装app的签名和已经存在的使用相同UID的app签名相同。 - createDataDirsLI()方法创建app的数据目录,例如: /data/data/${package_name}/
- adjustCpuAbisForSharedUserLPw()方法保证如果新安装的app使用了”android:sharedUID”, 它会将它的abi调整成和系统中使用相同uid的其它app一样的abi
- 接下来就会在mSettings中写入正在安装app的信息,比如安装路径,数据目录路径,签名信息,abi信息等。最终将这些信息存在/data/system/packages.xml中。同时还会将AndroidManifest.xml中的四大组件以及权限等信息,存到PackageManagerService的对应数组里,
这样以后通过startActivity之类的方法打开相应的组件时,就可以找到相应的组件是否存在。需要注意的是,这些四大组件以及权限信息,只是保存在内存中,每次系统开机时,都会重新扫描apk文件,然后将它们再次存到内存中使用。
3. 小结
关于新安装一个apk的大概流程就介绍完了;当然Android系统因为要兼容各种各样的情况,关于安装apk的代码实际上比上面分析的更加的复杂,但是无论有多少if else,核心原理和流程还是和上面分析的一样。希望大家能够对着代码自己弄解清楚。
更多相关文章
- Android(安卓)webview数据获取 webview抓取
- Android(安卓)Ril库总结
- Android:阴影效果的另一种实现方法:layer-list
- Android(安卓)Studio 启动安卓模拟器时,报VT-x is disabled in BI
- Android(安卓)DataBinding
- Android深入理解JNI(二)类型转换、方法签名和JNIEnv
- android studio 学习入门篇(转载)
- android菜单中的见解
- Android(安卓)WebViewClient 处理跳转URL