Android分包MultiDex源码分析 联系客服

发布时间 : 星期六 文章Android分包MultiDex源码分析更新完毕开始阅读8584ce55f11dc281e53a580216fc700abb685230

* @throws IOException */

private static void installSecondaryDexes(ClassLoader loader, File dexDir, List files) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException, IOException {

if (!files.isEmpty()) {

//对不同版本的SDK做不同处理 if (VERSION.SDK_INT >= 19) {

MultiDex.V19.install(loader, files, dexDir); } else if (VERSION.SDK_INT >= 14) {

MultiDex.V14.install(loader, files, dexDir); } else {

MultiDex.V4.install(loader, files); } }

}

可以看到,对于不同的SDK版本,分别采用了不同的处理方法,我们主要分析SDK>=19的情况,其他情况大同小异,读者可以自己去分析。

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

/**

* 安装dex文件 *

* @param loader 类加载器 * @param additionalClassPathEntries 需要安装的dex

* @param optimizedDirectory 缓存目录,用以存放opt之后的dex文件 * @throws IllegalArgumentException * @throws IllegalAccessException * @throws NoSuchFieldException * @throws InvocationTargetException * @throws NoSuchMethodException */

private static void install(ClassLoader loader,

List additionalClassPathEntries, File optimizedDirectory) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException,

InvocationTargetException, NoSuchMethodException {

//通过反射获取ClassLoader对象中的pathList属性,其实是ClassLoader的父类BaseDexClassLoader中的成员

Field pathListField = MultiDex.findField(loader, \ //通过属性获取该属性的值,该属性的类型是DexPathList Object dexPathList = pathListField.get(loader);

ArrayList suppressedExceptions = new ArrayList();

//通过反射调用dexPathList的makeDexElements返回Element对象数组。方法里面会读取每一个输入文件,生成DexFile对象,并将其封装进Element对象 Object[] elements = makeDexElements(dexPathList, new ArrayList(additionalClassPathEntries), optimizedDirectory, suppressedExceptions);

//将elements数组跟dexPathList对象的dexElements数组合并,并把合并后的数组作为dexPathList新的值

MultiDex.expandFieldArray(dexPathList, \

//处理异常

if (suppressedExceptions.size() > 0) {

Iterator suppressedExceptionsField = suppressedExceptions.iterator();

while (suppressedExceptionsField.hasNext()) {

IOException dexElementsSuppressedExceptions = (IOException) suppressedExceptionsField.next();

Log.w(\\in makeDexElement\dexElementsSuppressedExceptions); }

Field suppressedExceptionsField1 = MultiDex.findField(loader, \

IOException[] dexElementsSuppressedExceptions1 = (IOException[]) ((IOException[]) suppressedExceptionsField1.get(loader));

if (dexElementsSuppressedExceptions1 == null) { dexElementsSuppressedExceptions1 = (IOException[]) suppressedExceptions.toArray(new IOException[suppressedExceptions .size()]); } else {

IOException[] combined = new IOException[suppressedExceptions.size() + dexElementsSuppressedExcewww.shanxiwang.netptwww.sm136.comions1.length]; suppressedExceptions.toArray(combined);

System.arraycopy(dexElementsSuppressedExceptions1, 0, combined, suppressedExceptions.size(), dexElementsSuppressedExceptions1.length); dexElementsSuppressedExceptions1 = combined;

}

suppressedExceptionsField1.set(loader, dexElementsSuppressedExceptions1); }

}

private static Object[] makeDexElements(Object dexPathList,

ArrayList files, File optimizedDirectory, ArrayList suppressedExceptions) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { Method makeDexElements = MultiDex.findMethod(dexPathList, \

return (Object[]) ((Object[]) makeDexElements.invoke(dexPathList, new Object[]{files, optimizedDirectory, suppressedExceptions})); } }

在Android中,有两个ClassLoader,分别是DexPathList和PathClassLoader,它们的父类都是BaseDexClassLoader,DexPathList和PathClassLoader的实现都是在BaseDexClassLoader之中,而BaseDexClassLoader的实现又基本是通过调用DexPathList的方法完成的。DexPathList里面封装了加载dex文件为DexFile对象(调用了native方法,有兴趣的童鞋可以继续跟踪下去)的方法。 上述代码中的逻辑如下:

通过反射获取pathList对象

通过pathList把输入的dex文件输出为elements数组,elements数组中的元素封装了DexFile对象

把新输出的elements数组合并到原pathList的dexElements数组中 异常处理

当把dex文件加载到pathList的dexElements数组之后,整个multidex.install基本上就完成了。

但可能还有些童鞋还会有些疑问,仅仅只是把Element数组合并到ClassLoader就可以了吗?还是没有找到加载类的地方啊?那我们再继续看看,当用到一个类的时候,会用ClassLoader去加载一个类,加载类会调用类加载器的findClass方法

@Override

protected Class findClass(String name) throws ClassNotFoundException { List suppressedExceptions = new ArrayList(); //调用pathList的findClass方法

Class c = pathList.findClass(name, suppressedExceptions); if (c == null) {

ClassNotFoundException cnfe = new ClassNotFoundException(\find class \\\

for (Throwable t : suppressedExceptions) { cnfe.addSuppressed(t); }

throw cnfe; }

return c; }

于是继续跟踪:

public Class findClass(String name, List suppressed) { //遍历dexElements数组

for (Element element : dexElements) {

DexFile dex = element.dexFile; if (dex != null) {

//继续跟踪会发现调用的是一个native方法

Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed); if (clazz != null) { return clazz; } } }

if (dexElementsSuppressedExceptions != null) {

suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); }

return null; }

到现在就清晰了,当加载一个类的时候,会遍历dexElements数组,通过native方法从Element元素中加载类名相应的类 总结

到最后,总结整个multidex.install流程,其实很简单,就做了一件事情,把apk中的secondary dex文件通过ClassLoader转换成Element数组,并把输出的数组合与ClassLoader的Element数组合并。