MultiDex
因此,分包库只针对Dalvik, 即5.0以下
- 默认情况下,Dalvik 限制应用的每个 APK 只能使用单个 classes.dex 字节码文件,
- 将/data/app/apkName.apk路径下解压得到的classes2.dex, …, classesN.dex,依次写入到/data/data/pkgName/code_cache/secondary-dexes/apkName.apk.classes2.zip等zip文件的classes.dex中,并返回这个zip列表
- 将这个要安装的zip列表加入BaseDexClassLoader的pathList实例的dexElements数组中,其中会针对各dex文件进行dex2opt优化
- 一旦加入到了dexElements数组中,程序启动的时候,ClassLoader会加载dexElements数组中的元素,从而实现multi dex的安装
- Android 5.0(API 级别 21)及更高版本使用名为 ART 的运行时,后者原生支持从 APK 文件加载多个 DEX 文件。
- ART 在应用安装时执行预编译,扫描 classesN.dex 文件,并将它们编译成一个 .oat 文件,供 Android 设备执行。
- 因此,minSdkVersion 为 21 或更高值,则不需要 Dalvik 可执行文件分包支持库。
简而言之就是在dalvik,多个dex会通过multidex将各个dex加入dexElements中并且进行dexopt, 在art是多个dex编译成一个oat文件
Android Build System
- compile source code to Dex
- APK packageer combines Dex files and compiled resources into a single APK
- APK Packager sings APK
- Before generating the final APK, optimze the code to avoid the unused code
Dex文件
- Dex文件是Android平台上和传统Class文件对应的Java字节码文件
- Dex文件核心内容和传统文件类似,只不过对移动设备做了一些定制化处理
- ARM cpu显著特点:通用寄存器比较多
android安装apk过程
- Android系统通过PackageManagerService来安装APK,在安装的过程,PackageManagerService会通过另外一个类Installer的成员函数dexopt来对APK里面的dex字节码进行优化:
- Installer通过socket向守护进程installd发送一个dexopt请求,这个请求是由installd里面的函数dexopt来处理的。
- 但是!!android系统它只会将主dex编译成odex,不能将子dex也变成odex加载进内存中
- 在应用启动的回调方法中,将其他的子dex文件手动解压、编译、加载进内存中—google官方做法
从安装过程上来看
Java的代码实际上需要两次“转换”才可以在android设备上运行
一.PC端:.class->.dex->.apk
二.phone:dex->odex
区别在于第二步。
ART : .dex->.odex(机器码)(AOT Ahead-Of-Time)
Dalvik: .dex->.odex(字节码)(JIT Just-In-Time)
- 执行 ODEX 文件的效率会比直接执行 Dex 文件的效率要高很多
机器码可直接执行,而字节码每次启动都需要执行将优化过的odex字节码再转换成机器码
dexopt
oat, It’s Of Ahead Time, a silly reordering of Ahead Of Time.
info | dexopt | dex2oat |
---|---|---|
compile | JIT | AOT |
output | odex (optimized dex) file | 生成物每个版本不同 OAT file(.oat, may contain machine code in the ELF format.)文件名可能是dex或者odex,但是其实是oat文件 Android O有三个: - .vdex: contains the uncompressed DEX code of the APK, with some additional metadata to speed up verification. - .odex: contains AOT compiled code for methods in the APK. - .art (optional): contains ART internal representations of some strings and classes listed in the APK, used to speed application startup. |
optimized | replacing a virtual invoke instruction with an optimized version that includes the vtable index of the method being called | 直接转机器码 |
Both tools are normally run at install time on the device.
dex file
逻辑上,可以把dex文件分成3个区,头文件、索引区和数据区。索引区的ids后缀为identifiers的缩写。
odex file
[ODEX格式及生成过程]link
- Apk在安装(installer)时,就会进行验证和优化,目的是为了校验代码合法性及优化代码执行速度
- 验证和优化后,会产生ODEX文件,运行Apk的时候,直接加载ODEX,避免重复验证和优化,加快了Apk的响应时间。
- 一. 首先将一个空的DexOptHeader写入ODEX文件
- 二. 从Apk中提取classes.dex,追加到ODEX文件
- 三. 修改Dex内容
- 四. 因为3修改了Dex内容,所以要重新计算Dex的checksum
- 五. 往ODEX文件后面追加Dependenices内容(是指Dex文件之间的依赖)
- 六. 将优化的其他内容追加到ODEX文件
- 七. 根据所有的内容,改写第一步中DexOptHeader的相关字段值。
oat文件
- OAT文件本质上是一个ELF文件,因此在最外层它具有一般ELF文件的结构,例如它有标准的ELF文件头以及通过段(Section)来描述文件内容。
- OAT文件包含有两个特殊的段oatdata和oatexec,前者包含有用来生成本地机器指令的dex文件内容,后者包含有生成的本地机器指令,它们之间的关系通过储存在oatdata段前面的oat头部描述。
64K方法数Dex分包优化方案
Android中dex文件的加载与优化流程
dex2oat的原理及慢的原因
why 65536
- 早期的Android系统中,DexOpt有一个问题,DexOpt会把每一个类的方法id检索起来,存在一个链表结构里面。但是这个链表的长度是用一个short类型来保存的,导致了方法id的数目不能够超过65536个。
- invoke-kind (调用各类方法)指令中,方法引用索引数是 16 位的,也就是最多调用 2^16 = 65536 个方法,这就是 DexFormat 中 MAX_MEMBER_IDX 为 0xFFFF 的原因。
- 其实本质是dalvik指令的限制
- 新版本的Android系统中,DexOpt修复了这个问题,但是我们仍然需要对低版本的Android系统做兼容
multidex工作流程
MultiDex的工作流程具体分为两个部分
- 一个部分是打包构建Apk的时候,将Dex文件拆分成若干个小的Dex文件,这个Android Studio已经帮我们做了(设置 “multiDexEnabled true”)
- 另一部分就是在启动Apk的时候,同时加载多个Dex文件(具体是加载Dex文件优化后的Odex文件,不过文件名还是.dex),这一部分工作从Android 5.0开始系统已经帮我们做了,但是在Android 5.0以前还是需要通过MultiDex Support库来支持(MultiDex.install(Context))。
multidex
- before Android 5.0 (API level 21) —you can’t use more than one bytecode file per APK.
- Android version 5.0 (API level 21) or higher. uses a runtime called ART which supports loading multiple DEX files from APK files
- If you do not override the Application class
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<application
android:name="android.support.multidex.MultiDexApplication" >
...
</application>
</manifest>
- If you do override the Application class
public class MyApplication extends MultiDexApplication { ... }
- Or if you do override the Application class but it’s not possible to change the base class[也可以异步线程优化]
public class MyApplication extends SomeOtherApplication {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
multidex 安装过程
分析MultiDex的的入口就是它的静态方法install()。 这个方法的作用就是把从应用的APK文件中的dex添加到应用的类加载器PathClassLoader中的DexPathList的Emlement数组中。
- 通过一定的方式把dex文件抽取出来,然后把这些dex文件追加到DexPathList的Element[]数组的后面,这个过程要尽可能的早,所以一般是在Application的attachBaseContext()方法中。
- 一些热修复技术,就是通过一定的方式把修复后的dex插入到DexPathList的Element[]数组前面,实现了修复后的class抢先加载。