众所周知JVM执行的是.class字节码,而Android的Dalvik或ART是不能直接运行.class文件,运行的是编译生成的dex文件,并且不同版本的Android在运行时对dex的编译策略也存在着变化。在Android4.4之前Android虚拟机是Dalvik,在Android2.2到Android4.4时期,其执行的时候采用的是JIT编译,当App运行时,每当遇到一个新类,JIT编译器就会对这个类进行即时编译,经过编译后的代码,会被优化成相当精简的原生型指令码,这样在下次执行到相同逻辑的时候,速度就会更快;在Android4.4的时候Google推出了新的虚拟机运行时环境ART,在Android7.0之前,其采用的是AOT编译,应用在安装的时候会启动 dex2oat,把 dex预编译成 ELF 文件,每次运行程序的时候不用重新编译。以上两种方式存在着缺陷很明显:JIT每次程序启动后都要重新编译,在运行时也会额外对性能消耗;AOT在apk安装时要消耗大量时间,且编译优化后的文件又会额外占用磁盘空间。所以在Android7.0时采用AOT/JIT 混合编译的策略:apk安装时dex文件不会被编译,app运行时dex文件会解释执行,热点函数被JIT编译后缓存并生成profile文件,在手机空闲或者充电时进行AOT编译。
本文将基于Android10的源码分析Apk安装过程中,对dex的编译处理;并给出一种基于热代码配置文件的安装方案。
dex2oat
dex编译优化成为运行时执行的oat的核心是dex2oat,所以首先介绍一下dex2oat。dex2oat是ART中把dex文件编译成oat的工具,位置在手机的/system/bin/dex2oat,源码在art/dex2oat/Dex2oat.cc。执行dex2oat编译时的编译选项中有compile-filter,从 Android O 开始,有四个官方支持的过滤器:
verify:只运行 DEX 代码验证。
quicken:运行 DEX 代码验证,并优化一些 DEX 指令,以获得更好的解译器性能。
speed:运行 DEX 代码验证,并对所有方法进行 AOT 编译。
speed-profile:运行 DEX 代码验证,并对配置文件中列出的方法进行 AOT 编译。
apk安装
接下来我们分析Android安装apk时的流程,apk安装可以分为四步:将APK的信息通过IO流的形式写入到PackageInstaller.Session中;调用PackageInstaller.Session的commit方法,将APK的信息交由PKMS处理;拷贝Apk文件到/data/app等目录;执行安装。
我们跳过前面的步骤,直接分析真正安装的代码。复制完Apk后调用HandlerParams,执行PackageManagerService中的安装方法。
//PackageManagerService.java
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
if (args.mMultiPackageInstallParams != null) {
args.mMultiPackageInstallParams.tryProcessInstallRequest(args, currentStatus);
} else {
//普通安装
PackageInstalledInfo res = createPackageInstalledInfo(currentStatus);
processInstallRequestsAsync(res.returnCode == PackageManager.INSTALL_SUCCEEDED, Collections.singletonList(new InstallRequest(args, res)));
}
}
//processInstallRequestsAsync中会调用到installPackagesLI
private void installPackagesLI(List<InstallRequest> requests) {
//分析安装包并对其进行初始验证
final PrepareResult prepareResult = preparePackageLI(request.args, request.installResult);
final List<ScanResult> scanResults = scanPackageTracedLI(
prepareResult.packageToScan, prepareResult.parseFlags,
prepareResult.scanFlags, System.currentTimeMillis(),
request.args.user);
ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
installResults,
prepareResults,
mSharedLibraries,
Collections.unmodifiableMap(mPackages), versionInfos,
lastStaticSharedLibSettings);
commitRequest = new CommitRequest(reconciledPackages, sUserManager.getUserIds());
commitPackagesLocked(commitRequest);
//执行完成Apk安装
executePostCommitSteps(commitRequest);
}
private void executePostCommitSteps(CommitRequest commitRequest) {
for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
//根据解析的包准备完数据进行安装
prepareAppDataAfterInstallLIF(pkg);
//如果需要替换安装,则需要清楚原有的APP数据
if (reconciledPkg.prepareResult.clearCodeCache) {
clearAppDataLIF(pkg