文章目录
一、65535 方法数限制和 MultiDex 配置
二、多 DEX 加载引入
三、PathClassLoader 类加载源码分析
四、BaseDexClassLoader 类加载源码分析
五、DexPathList 相关载源码分析
六、多 DEX 存放位置
一、65535 方法数限制和 MultiDex 配置
在 Android 开发中 , 尤其是项目比较大时 , 或引入的依赖库过多 , 一般的项目后期都会遇到 如下问题 , 整个工程的方法数超过了 65535 6553565535 个 , 编译时报如下错误 :
trouble writing output: Too many field references: 131000; max is 65536. You may try using --multi-dex option. Conversion to Dalvik format failed: Unable to execute dex: method ID not in [0, 0xffff]: 65536
Google 官方给出了解决方案 : https://developer.android.google.cn/studio/build/multidex.html
5.0 5.05.0 之前的版本 , 应用中只能使用单个 classes.dex , 5.0 5.05.0 及以后的版本 原生支持多个 classes.dex 文件 ;
如果最低版本号大于等于 21 2121 , 就可以不用配置 MultiDex ;
反之版本号小于 21 2121 , 则 必须进行 MultiDex 配置 ;
Google 提供的 com.android.support:multidex 库 , 就是为了兼容 5.0 5.05.0 之前的版本 ;
二、多 DEX 加载引入
MultiDexApplication 中就使用 Android 中的类加载机制 , ClassLoader 加载机制 ;
ClassLoader 相关源码在 libcore/dalvik/src/main/java/dalvik/system Android 源码目录下 ,
参考 : libcore/dalvik/src/main/java/dalvik/system
常用的 ClassLoader 是 PathClassLoader 和 DexClassLoader ;
PathClassLoader 只能加载 已安装 apk 的 dex ;
DexClassLoader 可以加载 已安装 或 文件系统 中的 APK , DEX , JAR ;
源码参考 :
PathClassLoader : libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java DexClassLoader : libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java
三、PathClassLoader 类加载源码分析
在 Android 中使用的是 PathClassLoader , 该类很简单 , 只是单纯的继承 BaseDexClassLoader ,
package dalvik.system; public class PathClassLoader extends BaseDexClassLoader { public PathClassLoader(String dexPath, ClassLoader parent) { super(dexPath, null, null, parent); } public PathClassLoader(String dexPath, String libraryPath, ClassLoader parent) { super(dexPath, null, libraryPath, parent); } }
参考源码地址 : libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java
四、BaseDexClassLoader 类加载源码分析
BaseDexClassLoader 是通过 Class<?> findClass(String name) 方法查找类的 , 给定一个全类名字符串 , 返回字节码 ,
查找类通过调用 pathList 的 findClass 方法 , 获取字节码文件 ,
pathList 对应的成员变量定义是 DexPathList pathList , 它是 DexPathList 类型的 ,
public class BaseDexClassLoader extends ClassLoader { private final DexPathList pathList; @Override protected Class<?> findClass(String name) throws ClassNotFoundException { List<Throwable> suppressedExceptions = new ArrayList<Throwable>(); Class c = pathList.findClass(name, suppressedExceptions); if (c == null) { ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList); for (Throwable t : suppressedExceptions) { cnfe.addSuppressed(t); } throw cnfe; } return c; } }
参考源码地址 : libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
五、DexPathList 相关载源码分析
DexPathList 的 findClass 方法 , 主要是遍历 Element[] dexElements 成员 ,
Element[] dexElements 数组中保存的就是内存中的 DEX 文件 , 如果 APP 中有 3 33 个 DEX 文件 , 那么该数组就有 3 33 个元素 ;
然后 获取该 element 中的 dexFile , 这是 DexFile 类型文件 ,
调用 DexFile 的 loadClassBinaryName 加载对应的类 ,
/*package*/ final class DexPathList { /** * dex/resource (class path) 元素集合. * 应该调用 pathElements , 但是 Facebook 应用通过反射修改 dexElements . */ private final Element[] dexElements; public Class findClass(String name, List<Throwable> suppressed) { for (Element element : dexElements) { DexFile dex = element.dexFile; if (dex != null) { Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed); if (clazz != null) { return clazz; } } } if (dexElementsSuppressedExceptions != null) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); } return null; } }
参考源码地址 : libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
六、多 DEX 存放位置
上述 DexPathList 中的 Element[] dexElements 成员就保存了当前应用中的 DEX 文件 ,
再次回到 classes.dex 分割的问题上 , 如果我们设置一个主 DEX , 其中只存在代理 Application , 用于解密其它被加密的 DEX , 其它的 DEX 都是被加密的 ;
这些 DEX 文件 都需要被存放到上述 DexPathList 的 Element[] dexElements 数组中 ;