简书链接:官方multidex的加载过程分析然后研究研究能否抄袭一点点技术
文章字数:557,阅读全文大约需要2分钟
1、检测jvm版本,正则 2.1.0 那么就是支持jvm,那么默认是不是进行dex的合并。
. doInstallation方法获取到了ApplicationInfo

1
2
3
4
5
mainContext: Context  = {AppContext@4800} 
sourceApk: File = {File@4821} "/data/app/cn.qssq666.robot-2/base.apk"
dataDir: File = {File@4822} "/data/user/0/cn.qssq666.robot"
secondaryFolderName: String = {@4823} "secondary-dexes"
prefsKeyPrefix: String = {@4824} ""

2、clearOldDexDir第二步检查包名/files/secondary-dexes/是否存在存在则删除。里面所有文件,还把文件夹也删除了.
. /data/user/0/cn.qssq666.robot/code_cache/secondary-dexes

检查是否更改,更改就走另外一个方法否则走performExtractions
大概逻辑是读取apk的classdex 2-最终的dex全部打包(extract方法打包)成zip文件
如先把classes2.dex解压打包成临时文件
/data/user/0/cn.qssq666.robot/code_cache/secondary-dexes/tmp-base.apk.classes238553433.zip
然后重命名为为/data/user/0/cn.qssq666.robot/code_cache/secondary-dexes/base.apk.classes2.zip

打包的代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
InputStream in = apk.getInputStream(dexFile);
ZipOutputStream out = null;
File tmp = File.createTempFile("tmp-" + extractedFilePrefix, ".zip", extractTo.getParentFile());
Log.i("MultiDex", "Extracting " + tmp.getPath());

try {
out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tmp)));

try {
ZipEntry classesDex = new ZipEntry("classes.dex");
classesDex.setTime(dexFile.getTime());
out.putNextEntry(classesDex);
byte[] buffer

从zipentry实体获取entry的输入流然后创建一个ZipOutputStream输出流,然后给他弄一个时间,然后zip的实体名字叫classes.dex也就是每一个zip文件都有一个classes.dex很显然,他的合并技术和我用的方法完全不同。是把一个包含多个dex的apk变成每一个apk,(当然这里叫zip)然后里面有一个classes.dex

image.png

最终的成果
image.png

然后
/data/user/0/cn.qssq666.robot/shared_prefs/multidex.version.xml
把时间戳和zip的crc校验码存起来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

private static void putStoredApkInfo(Context context, String keyPrefix, long timeStamp, long crc, List<MultiDexExtractor.ExtractedDex> extractedDexes) {
SharedPreferences prefs = getMultiDexPreferences(context);
Editor edit = prefs.edit();
edit.putLong(keyPrefix + "timestamp", timeStamp);
edit.putLong(keyPrefix + "crc", crc);
edit.putInt(keyPrefix + "dex.number", extractedDexes.size() + 1);
int extractedDexId = 2;

for(Iterator var10 = extractedDexes.iterator(); var10.hasNext(); ++extractedDexId) {
MultiDexExtractor.ExtractedDex dex = (MultiDexExtractor.ExtractedDex)var10.next();
edit.putLong(keyPrefix + "dex.crc." + extractedDexId, dex.crc);
edit.putLong(keyPrefix + "dex.time." + extractedDexId, dex.lastModified());
}

edit.commit();
}

这才完成了

1
2
List<? extends File> files = MultiDexExtractor.load(mainContext, sourceApk, dexDir, prefsKeyPrefix, false);

的逻辑
然后走
installSecondaryDexes(loader, dexDir, files);

具体逻辑

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
    }

private static final class V19 {
private V19() {
}

private static void install(ClassLoader loader, List<? extends File> additionalClassPathEntries, File optimizedDirectory) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
Field pathListField = MultiDex.findField(loader, "pathList");
Object dexPathList = pathListField.get(loader);
ArrayList<IOException> suppressedExceptions = new ArrayList();
MultiDex.expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList, new ArrayList(additionalClassPathEntries), optimizedDirectory, suppressedExceptions));
if (suppressedExceptions.size() > 0) {
Iterator var6 = suppressedExceptions.iterator();

while(var6.hasNext()) {
IOException e = (IOException)var6.next();
Log.w("MultiDex", "Exception in makeDexElement", e);
}

Field suppressedExceptionsField = MultiDex.findField(dexPathList, "dexElementsSuppressedExceptions");
IOException[] dexElementsSuppressedExceptions = (IOException[])((IOException[])suppressedExceptionsField.get(dexPathList));
if (dexElementsSuppressedExceptions == null) {
dexElementsSuppressedExceptions = (IOException[])suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
} else {
IOException[] combined = new IOException[suppressedExceptions.size() + dexElementsSuppressedExceptions.length];
suppressedExceptions.toArray(combined);
System.arraycopy(dexElementsSuppressedExceptions, 0, combined, suppressedExceptions.size(), dexElementsSuppressedExceptions.length);
dexElementsSuppressedExceptions = combined;
}

suppressedExceptionsField.set(dexPathList, dexElementsSuppressedExceptions);
}

}

private static Object[] makeDexElements(Object dexPathList, ArrayList<File> files, File optimizedDirectory, ArrayList<IOException> suppressedExceptions) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Method makeDexElements = MultiDex.findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class, ArrayList.class);
return (Object[])((Object[])makeDexElements.invoke(dexPathList, files, optimizedDirectory, suppressedExceptions));
}
}
}

很遗憾,这个v19的代码在我的23的手机并不能执行,
因为PathClassloader没有makeDexElements方法。

这里就不继续探索了,看看源码有哪几个把,copy过来改一下看看

image.png

首先要让IS_VM_MULTIDEX_CAPABLE变成非常量,这样才好玩嘛。