概述
Android中应用的安装方式有多种:
- 系统应用安装,这种方式在第一次开机时完成。这部分在上一篇博客中已经讲到。
- 通过系统安装器安装,这种方式有UI界面交互。
- 通过
adb install
命令安装。 - 通过应用市场安装。
虽然有这么多的安装方式,但在PMS内部,后面三种的流程却是大同小异的。
我们先来看一下ApplicationPackageManager
给我们开发的安装应用的接口:
除了ApplicationPackageManager.installExistingPackage()
之外,其他的几个方法都会通过mPM.installPackage()
通过跨进程调用到PMS的installPackage()
方法,因此,Android应用安装部分的介绍我们就从这个方法来说起。
Android的应用安装有下面几个阶段的工作:
- 复制文件阶段
- 将文件复制到/data/app/目录
- 解析应用阶段
- 解析apk信息
- dexopt操作
- 更新权限信息
- 完成安装,发送
Intent.ACTION_PACKAGE_ADDED
广播
代码调用流程如下:
1 | ├── PMS.installPackage() |
adb install
安装 APK 命令可以用adb install [-lrtsd] <file>
或者adb install-multiple [-lrtsdp] <file...>
,adb install-multiple
表示批量安装。
参数介绍:
- -l:锁定该程序
- -r:可以覆盖已有的应用,保留数据和缓存文件
- -t:允许测试该应用
- -s:安装在SD卡中
- -d:允许降低版本安装
- -p:部分应用程序安装
安装
复制文件阶段
这个阶段的主要工作就是把要安装的应用复制到/data/app目录下,这里主要针对安装第三方应用以及升级系统应用而言,系统应用的安装在初始化部分完成的,前面博客已经做过介绍。
下面开始分析复制文件过程的源码,首先是从PMS.installPackageAsUser()
开始。
1 |
|
installPackageAsUser()
主要工作就是进行一系列权限的验证,然后发送 INIT_COPY
消息。
发送消息时会传递一个InstallParams
参数,InstallParams
是继承自HandlerParams
抽象类的,用来记录安装应用的参数。
下面再来看一下对INIT_COPY
消息的处理,该消息的处理在PackageHandler.doHandleMessage()
中进行的。
1 | case INIT_COPY: { |
对INIT_COPY
消息的处理就是要绑定DefaultContainerService
服务,这个服务执行一些针对的文件 copy 等相关工作,然后把消息传递过来的InstallParams
参数保存到mPendingInstalls
列表。如果原来没有进行过绑定服务工作,那么就等待服务绑定好后在onServiceConnected()
发送MCS_BOUND
消息,并且传递onServiceConnected()
参数中的Binder
对象。如果已经连接好服务,当前只有一个安装请求那么就直接发送该消息,如果还有其他的请求,就需要进行排队处理,等待其他请求的MCS_BOUND
消息处理完之后再往下接着处理。
下面来看一下对MCS_BOUND
消息的处理:
1 | case MCS_BOUND: { |
对MCS_BOUND
消息的处理主要就是调用HandlerParams
的startCopy()
方法开始执行安装任务,并判断安装队列中是否有其他任务,如果有就重复发送MCS_BOUND
消息,没有的话就发送MCS_UNBIND
消息。
对于mContainerService
,上面我们知道,它是作为参数在onServiceConnected()
发送过来的,它的实现在DefaultContainerService
的mBinder
。
下面来看一下对MCS_UNBIND
消息的处理:
1 | case MCS_UNBIND: { |
下面来开始分析HandlerParams
的startCopy()
方法的处理过程:
1 | final boolean startCopy() { |
下面来开发分析InstallParams
的handleStartCopy()
方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public void handleStartCopy() throws RemoteException {
// 首先对安装的标志位进行判断,如果既有内部安装标志,又有外部安装标志,那么就设置
//PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION返回值
......
//否则就调用mContainerService.getMinimalPackageInfo()来获取一些安装包的信息包括包大小,包名等等
} else {
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
packageAbiOverride);
//如果空间不足,先释放部分缓存,如果还是不够,放弃此次安装
if (!origin.staged && pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
final StorageManager storage = StorageManager.from(mContext);
final long lowThreshold = storage.getStorageLowBytes(
Environment.getDataDirectory());
......
}
}
if (ret == PackageManager.INSTALL_SUCCEEDED) {
int loc = pkgLite.recommendedInstallLocation;
...
// 这里处理一些对安装位置的校验
}
// 这里会根据`InstallParams`生成一个`InstallArgs`对象
final InstallArgs args = createInstallArgs(this);
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
......
final int requiredUid = mRequiredVerifierPackage == null ? -1
: getPackageUid(mRequiredVerifierPackage, userIdentifier);
if (!origin.existing && requiredUid != -1
&& isVerificationEnabled(userIdentifier, installFlags)) {
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
...
// 执行一些校验工作,校验成功之后在CHECK_PENDING_VERIFICATION消息处理方法中执行InstallArgs.copyApk()
} else {
// 如果无需校验,执行InstallArgs.copyApk()方法继续
ret = args.copyApk(mContainerService, true);
}
}
mRet = ret;
}
handleStartCopy()
执行的工作如下:
- 判断安装标志位是否合法
- 获取安装包的一些信息
- 判断安装空间是否足够
- 对安装位置的校验
- 判断是否需要对应用进行校验工作
- 如果校验成功,执行
InstallArgs.copyApk()
- 如果无需校验,直接执行
InstallArgs.copyApk()
InstallArgs
是个抽象类,一共有三个实现类MoveInstallArgs
(针对已有文件的Move)、AsecInstallArgs
(针对SD卡)和FileInstallArgs
(针对内部存储),会在createInstallArgs()
方法中根据不同的参数返回不同的实现类。
接下来分析FileInstallArgs.copyApk()
方法:
1 | int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { |
复制完成后,apk会被复制到/data/app/目录下面。
解析应用阶段
这个阶段的工作是对安装包进行扫描优化,把应用转换成oat格式,然后装载到内存中去。
在handleStartCopy()
执行完之后,文件复制工作阶段的工作已经完成了,接下来会在startCopy()
中调用handleReturnCode()
->processPendingInstall()
来进行应用的解析和装载。
1 | private void processPendingInstall(final InstallArgs args, final int currentStatus) { |
processPendingInstall()
方法内部是以异步的方式继续执行安装工作的,首先来调用installPackageLI()
执行安装工作,然后调用doPostInstall()
对前面的工作的返回结果进行处理,如果没有安装成功,执行清除的工作。然后再执行备份操作。
下面来看一下installPackageLI()
方法:
1 | private void installPackageLI(InstallArgs args, PackageInstalledInfo res) { |
installPackageLI()
方法首先解析apk安装包,然后判断当前是否有安装该应用,然后根据不同的情况进行不同的处理,然后进行Dex优化操作。如果是升级安装,调用replacePackageLI()
。如果是新安装,调用installNewPackageLI()
。这两个方法会在下面详细介绍。
processPendingInstall()
方法中执行安装的最后是发送POST_INSTALL
消息,现在来看一下这个消息需要处理的事情:
1 | case POST_INSTALL: { |
对POST_INSTALL
消息消息的处理主要就是一些权限处理、发送广播、通知相关应用处理安装结果,然后调用回调函数onPackageInstalled(),这个回调函数是调用installPackage()方法时作为参数传递进来的。
总结一下解析应用阶段的工作:
- 解析apk信息
- dexopt操作
- 更新权限信息
- 完成安装,发送
Intent.ACTION_PACKAGE_ADDED
广播
其他相关方法分析
PackageManagerService获取新的apk目录名字
1 | private File getNextCodePath(File targetDir, String packageName) { |
类似com.android.browser-1
replacePackageLI()
1 | private void replacePackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags, |
replaceSystemPackageLI()
1 | private void replaceSystemPackageLI(PackageParser.Package deletedPackage, |
replaceNonSystemPackageLI()
1 | private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage, |
installNewPackageLI()
1 | private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags, |