APK的安装过程
常见的APK安装方式有以下几种:
- 开机扫描时安装:开机时通过PMS的包扫描动作完成
- 下载后点击apk文件安装:用户自行下载安装包,通过点击安装包触发安装
- adb install安装:使用shell命令安装
- 从market安装:直接使用play store等安装
以下以下载后点击apk文件安装为例
当下载apk 到手机再点击apk 安装时,GooglePackageInstaller.apk会弹出一个是否安装界面,即PackageInstallerActivity。当用户点击安装时,就跳转到安装进度信息界面,即InstallAppProgress。这个过程会调用PMS解析下载的apk的基本信息,并保存到InstallParams 中,并发送INIT_COPY 消息,进入安装并一直显示直到安装结束。
第一阶段:复制apk文件
将需要安装apk的信息保存到InstallParams 中,并发送INIT_COPY 消息,这个过程是GooglePackageInstaller.apk调用PMS服务向PMS主进程发送INIT_COPY 消息。PMS 进程handle 会处理INIT_COPY消息,并连接IMediaContainerService服务,当连接成功,就会将消息对应的InstallParams加入一个列表,并发送MCS_BOUND 消息,处理这个消息时就开始apk 复制。
这个过程耗时比较短,而且只要点击安装新的apk就会向PMS主线程发送消息,这个过程没有互斥锁。
第二阶段:装载安装应用
当apk 复制完成,就会进入安装对应apk的过程,这个过程是通过创建一个新的进程runnable,并作为一个消息发post 到PMS所在主进程消息队列中。由于这些线程是异步的,并持有mInstallLock锁,所以如果同时安装多个apk其执行过程是不能并行的。必须上一个应用安装完成下一个应用线程才能启动。
这个过程主要有:包扫描收集组件信息并更新系统数据结构、apk需要空间信息的处理过程、
dexopt 优化过程及链接库处理过程。这些是在一个线程中完成,并持有mInstallLock 锁。当这些过程处理完,apk安装即将完成。
数据对比
安装耗时
表一
表一是S31(1G/16G) userdebug 版本恢复出厂设置后微信、qq、qq game 三个应用同时安装的时间,同样条件下单独安装这三个apk 得到的数据相差很小,可能在手机ram 足够大时同时安装几个应用影响很小,需要在手机较忙时才有明显的可比性。
System 进程堆栈
adb shell dumpsys meminfo system_server
同时安装 微信、qq、qq game 获取system_server堆栈值
App Summary
Pss(KB)
------
Java Heap: 30740
Native Heap: 12040
Code: 10136
Stack: 892
Graphics: 1336
Private Other: 5588
System: 2637
TOTAL: 63369 TOTAL SWAP (KB): 23952
单独安装微信获取system_server堆栈值。
App Summary
Pss(KB)
------
Java Heap: 25740
Native Heap: 11936
Code: 12012
Stack: 932
Graphics: 1472
Private Other: 3808
System: 4386
TOTAL: 60286 TOTAL SWAP (KB): 24040
空闲时获取system_server堆栈值。
App Summary
Pss(KB)
------
Java Heap: 19900
Native Heap: 13652
Code: 10576
Stack: 956
Graphics: 7504
Private Other: 3728
System: 4116
TOTAL: 60432 TOTAL SWAP (KB): 24320
多个apk 安装慢影响项
1:apk 安装时,dexopt 是一个重要耗时项。
2:同时安装apk时,会对每个apk开启一个新的安装线程,同时线程均持有mInstallLock 互斥锁, 开启多线程开销从而对apk安装有一定的影响。
3:多个apk安装会增加 PMS 主线程消息处理消耗,从而对apk安装有一定的影响。
4:同时安装多个apk时,java heap 占用量变化相对明显,在手机较忙时影响大。
微信:
04-07 18:16:04.135820 1072 1137 I PackageManager: init_copy idx=0: InstallParams{72a2f6d file=/data/app/
04-07 18:16:05.087538 1072 1137 D PackageManager: begin to process post message time :126955 installerPackageName:null
04-07 18:16:48.725164 1072 1137 D PackageManager: end to process post message time :170593 installerPackageName:Package{41d9339 com.tencent.mm}
53s 55 41s
04-07 18:16:49.888977 1072 1137 D PackageManager: begin to process post message time :171756
04-07 18:16:57.395392 1072 1137 D PackageManager: end to process post message time :179263 installerPackageName:Package{88331c1 com.facebook.katana}
28s -----51s 23s
04-07 18:16:59.269660 1072 1137 D PackageManager: begin to process post message time :181137
04-07 18:17:21.658882 1072 1137 D PackageManager: end to process post message time :203526 installerPackageName:Package{9740e34 com.afmobigroup.gphone}
38s ------70s 20s
04-07 18:17:23.484403 1072 1137 D PackageManager: begin to process post message time :205352
04-07 18:17:55.207531 1072 1137 D PackageManager: end to process post message time :237075 installerPackageName:Package{41828bd com.tencent.mobileqq}
68s ------75s 59s
04-07 18:17:55.462424 1072 1137 D PackageManager: begin to process post message time :237297
04-07 18:18:02.278030 1072 1137 D PackageManager: end to process post message time :244146 installerPackageName:Package{10c697 com.whatsapp}
60s ------70s 35s
04-07 18:16:04.135586 1072 1137 D PackageManager: PROCESS INIT_COPY
04-07 18:16:04.136210 1072 1137 I PackageManager: Trying to bind to DefaultContainerService
04-07 18:16:04.136910 1072 1117 W art : Long monitor contention with owner PackageInstaller (1222) at int com.android.internal.content.NativeLibraryHelper.nativeCopyNativeBinaries(long, java.lang.String, java.lang.String, boolean, boolean)(NativeLibraryHelper.java:-2) waiters=0 in boolean com.android.server.pm.PackageInstallerSession.isPrepared() for 563ms
04-07 18:16:26.869698 1072 1117 W art : Long monitor contention with owner PackageInstaller (1222) at java.lang.Object java.lang.Throwable.nativeFillInStackTrace!()(Throwable.java:-2) waiters=0 in boolean com.android.server.pm.PackageInstallerSession.isPrepared() for 11.210s
04-07 18:16:26.899029 1072 1086 W art : Long monitor contention with owner android.io (1117) at void com.android.server.pm.PackageInstallerService$2.run()(PackageInstallerService.java:556) waiters=0 in android.content.pm.IPackageInstallerSession com.android.server.pm.PackageInstallerService.openSessionInternal(int) for 11.238s
04-07 18:16:26.903281 1072 1085 W art : Long monitor contention with owner android.io (1117) at void com.android.server.pm.PackageInstallerService$2.run()(PackageInstallerService.java:556) waiters=1 in int com.android.server.pm.PackageInstallerService.createSessionInternal(android.content.pm.PackageInstaller$SessionParams, java.lang.String, int) for 2.646s
04-07 18:16:26.915094 2849 2849 D InstallAppProgress: Installation error code: 1
04-07 18:16:26.939220 2849 2849 D InstallAppProgress: Installation error code: 1
安装时间对比
多个应用安装耗时,从代码角度是计算的主要安装时间。
可以看出多个应用安装总用时比单个安装总用时要少。
同时有安装9个应用,实际只有5个安装了,apk实际点击安装的顺序跟截图上到下顺序一样。
有其它四个apk未安装的原因及安装实际耗时长如下log:
I PackageManager: Trying to bind to DefaultContainerService
W art : Long monitor contention with owner PackageInstaller (1222) at int com.android.internal.content.NativeLibraryHelper.nativeCopyNativeBinaries(long, java.lang.String, java.lang.String, boolean, boolean)(NativeLibraryHelper.java:-2) waiters=0 in boolean com.android.server.pm.PackageInstallerSession.isPrepared() for 563ms
W art : Long monitor contention with owner PackageInstaller (1222) at java.lang.Object java.lang.Throwable.nativeFillInStackTrace!()(Throwable.java:-2) waiters=0 in boolean com.android.server.pm.PackageInstallerSession.isPrepared() for 11.210s
W art : Long monitor contention with owner android.io (1117) at void com.android.server.pm.PackageInstallerService$2.run()(PackageInstallerService.java:556) waiters=0 in android.content.pm.IPackageInstallerSession com.android.server.pm.PackageInstallerService.openSessionInternal(int) for 11.238s
W art : Long monitor contention with owner android.io (1117) at void com.android.server.pm.PackageInstallerService$2.run()(PackageInstallerService.java:556) waiters=1 in int com.android.server.pm.PackageInstallerService.createSessionInternal(android.content.pm.PackageInstaller$SessionParams, java.lang.String, int) for 2.646s
D InstallAppProgress: Installation error code: 1
D InstallAppProgress: Installation error code: 1
1:apk copy 时包安装服务io操作太费时导致一直查询互斥对象锁,PackageInstaller 也会为下个apk安装不断查询互斥对象锁。
针对这一点可以修改同步锁类型,如将现在的一直查询改为未获取锁则睡眠等待通知,或者优化io读写速度。
安装消息处理:
1:MCS_UNBIND 、INIT_COPY 、MCS_BOUND
改变PMS消息队列消息处理顺序,比如当取到MCS_UNBIND,再查询后续5个消息是否有INIT_COPY 或MCS_BOUND消息,有则丢掉MCS_UNBIND 消息,继续处理后续消息,这样可以减少断开与连接DefaultContainerService开销。
参数调整
目前平台dalvik 参数如下:
dalvik.vm.dex2oat-filter verify-at-runtime
Dalvik.vm.usejit true
vmSafeMode false
以上参数都是比较优的参数配置
2:延迟MCS_UNBIND发送时间,现在是10s,或者发送跟POST_INSTALL (安装几乎完毕)消息建立一个时间关系。
3:复制apk优化,如buffer改变
4:certificate verification 优化
安装时间对比(N)
多个应用安装耗时,从代码角度是计算的主要安装时间。
可以看出多个应用安装总用时比单个安装总用时要少。
同时有安装9个应用,实际只有5个安装了,apk实际点击安装的顺序跟截图上到下顺序一样。
有其它四个apk未安装的原因及安装实际耗时长如下log:
I PackageManager: Trying to bind to DefaultContainerService
W art : Long monitor contention with owner PackageInstaller (1222) at java.lang.Object java.lang.Throwable.nativeFillInStackTrace!()(Throwable.java:-2) waiters=0 in boolean com.android.server.pm.PackageInstallerSession.isPrepared() for 11.210s
W art : Long monitor contention with owner android.io (1117) at void com.android.server.pm.PackageInstallerService$2.run()(PackageInstallerService.java:556) waiters=0 in android.content.pm.IPackageInstallerSession com.android.server.pm.PackageInstallerService.openSessionInternal(int) for 11.238s
D InstallAppProgress: Installation error code: 1
D InstallAppProgress: Installation error code: 1
1:包安装服务io操作太费时导致一直查询互斥对象锁,PackageInstaller 也会为下个apk安装不断查询互斥对象锁。
针对这一点可以修改同步锁类型,如将现在的一直查询改为未获取锁则睡眠等待通知,或者优化io读写速度。
安装消息处理:
1:MCS_UNBIND 、INIT_COPY 、MCS_BOUND
优化PMS消息队列消息处理,比如当取到MCS_UNBIND,再查询后续5个消息是否有INIT_COPY 或MCS_BOUND消息,有则丢掉MCS_UNBIND 消息,继续处理后续消息,这样可以减少断开与连接DefaultContainerService开销。
参数调整:
目前平台dalvik 参数如下:
dalvik.vm.dex2oat-filter verify-at-runtime
Dalvik.vm.usejit true
vmSafeMode false
以上参数都是比较优的参数配置,不宜修改。
frameworks\base\services\core\java\com\android\server\pm\PackageManagerServiceCompilerMapping.java
class PackageManagerServiceCompilerMapping {
// Names for compilation reasons.
static final String REASON_STRINGS[] = {
"first-boot", "boot", "install", "bg-dexopt", "ab-ota", "nsys-library", "shared-apk",
"forced-dexopt", "core-app"
};
Pm.dexopt.
frameworks\base\services\java\com\android\server\SystemServer.java
startOtherServices
-->
第一次启动或升级时:
public void updatePackagesIfNeeded() {
获取opt apk 和排序优先级
-->pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
-->performDexOpt(pkgs, mIsPreNUpgrade /* showDialog */,
getCompilerFilterForReason(causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT));
-->performDexOpt
-->performDexOptTraced
-->performDexOptTraced
-->performDexOptInternal
-->performDexOptInternalWithDependenciesLI
-->performDexOpt
-->performDexOptLI
包安装
installPackageLI
-->mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
null /* instructionSets */, false /* checkProfiles */,
getCompilerFilterForReason(REASON_INSTALL));
PMS
PackageManagerService
-->performDexOpt(coreApps, false,
getCompilerFilterForReason(REASON_CORE_APP));