MultiDex 运行过程

结构

MultiDex 代码比较少,关键类就三个。

分析

MultiDexApplication

1
2
3
4
5
6
7
8
9
public class MultiDexApplication extends Application {
public MultiDexApplication() {
}
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}

显然,关键得看 Multidex#install。

MultiDex

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void install(Context context) {
Log.i(TAG, "install");
// 1
if (IS_VM_MULTIDEX_CAPABLE) {
Log.i(TAG, "VM has multidex support, MultiDex support library is disabled.");
return;
}
// 2
if (Build.VERSION.SDK_INT < MIN_SDK_VERSION) {
throw new RuntimeException("Multi dex installation failed. SDK " + Build.VERSION.SDK_INT
+ " is unsupported. Min SDK version is " + MIN_SDK_VERSION + ".");
}
// 3
...
Log.i(TAG, "install done");
}
// 1

当虚拟机支持 MultiDex,直接打个 log 知会你一声,那么怎么判断当前虚拟机是否支持 MultiDex 呢?来看 IS_VM_MULTIDEX_CAPABLE:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static {
...
IS_VM_MULTIDEX_CAPABLE = isVMMultidexCapable(System.getProperty("java.vm.version"));
}
static boolean isVMMultidexCapable(String versionString) {
boolean isMultidexCapable = false;
if(versionString != null) {
Matcher matcher = Pattern.compile("(\\d+)\\.(\\d+)(\\.\\d+)?").matcher(versionString);
if(matcher.matches()) {
try {
int e = Integer.parseInt(matcher.group(1));
int minor = Integer.parseInt(matcher.group(2));
isMultidexCapable = e > 2 || e == 2 && minor >= 1;
} catch (NumberFormatException var5) {
;
}
}
}

静态代码块里拿到虚拟机版本号,正则判断版本是否大于 2.1,如果大于 2.1 就表明当前虚拟机默认支持 MultiDex。

那么有几个问题:

  • 什么样的虚拟机默认支持 MultiDex?
  • 为什么是 2.1?

第一个问题,ART 虚拟机默认支持 MultiDex,为什么?会在 ART 和 DAVILK 虚拟机的区别中解释。

第二个问题,来看看官方文档:如何判断当前是否是 ART 虚拟机

文档说,当 java.vm.version >= 2.0.0 则表明是 ART 虚拟机,可是 MultiDex 的代码里写的是 >= 2.1,为什么?应该是文档没更新。。。。

那么 java.vm.version 又是在哪里被设置的呢?来看看 java.lang.System

// 2

MultiDex 最低支持 Android 1.6

// 3

这部分是 MultiDex 的工作,仔细梳理。

1
2
3
4
5
6
7
8
9
10
11
12
ApplicationInfo applicationInfo = getApplicationInfo(context);
if (applicationInfo == null) {
return;
}
private static ApplicationInfo getApplicationInfo(Context context) {
try {
return context.getApplicationInfo();
} catch (RuntimeException e) {
return null;
}
}