更多内容,欢迎关注公众号: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,核心原理和流程还是和上面分析的一样。希望大家能够对着代码自己弄解清楚。

更多相关文章

  1. Android(安卓)webview数据获取 webview抓取
  2. Android(安卓)Ril库总结
  3. Android:阴影效果的另一种实现方法:layer-list
  4. Android(安卓)Studio 启动安卓模拟器时,报VT-x is disabled in BI
  5. Android(安卓)DataBinding
  6. Android深入理解JNI(二)类型转换、方法签名和JNIEnv
  7. android studio 学习入门篇(转载)
  8. android菜单中的见解
  9. Android(安卓)WebViewClient 处理跳转URL

随机推荐

  1. android 水平滚动 HorizontalScrollView
  2. android 定制对话框 Layoutlnflater
  3. 跨进程调用Service(AIDL服务) (附图 附源
  4. android之计时器chronometer
  5. Android消息机制---MessageQueue的工作原
  6. Android ApiDemos示例解析(33):App->Prefe
  7. android9.0 cdma 短信 发不出的问题
  8. Android启动画面Splash
  9. android之网格布局GridLayout
  10. Android(安卓)高仿微信实时聊天 基于百度