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,核心原理和流程还是和上面分析的一样。希望大家能够对着代码自己弄解清楚。

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值