发布时间 : 星期六 文章Android分包MultiDex源码分析更新完毕开始阅读8584ce55f11dc281e53a580216fc700abb685230
* @throws IOException */
private static void installSecondaryDexes(ClassLoader loader, File dexDir, List
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
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
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
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
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数组合并。