原理
先说一下Java的主动引用,主要场景有:
1、遇到new,getstatic,putstatic,invokestatic这4条字节码指令时,类的class如果没有被加载会先被ClassLoader加载到内存,然后才能创建对象,读取或设置静态字段,调用静态方法等。
2、反射,Class.forName()
3、子类初始化前会先初始化父类
4、包含main方法的类,虚拟机启动时会先初始化该类
5、JNI的方式:JNI.findClass()
每个class要使用时,也就是主动引用时会先通过ClassLoader把字节码文件信息加载到内存,Android也是类似,它默认使用dalvik.system.PathClassLoader类来加载class(dex文件)的,Android系统还提供了DexClassLoader类,开发者可以使用它来手动加载class(dex文件),PathClassLoader和DexClassLoader都继承了dalvik.system.BaseDexClassLoader类。
通过查看源码得知,BaseDexClassLoader类在构造方法里会实例化一个成员变量:DexPathList pathList,并将对应的dex文件目录传给pathList成员变量。事实上查找并解析class文件的操作都是在DexPathList类里面进行。DexPathList通过调用makePathElements()方法将dex、zip以及目录文件封装成Element实例,并存放到成员变量DexPathList.Element[] dexElements里。每次BaseDexClassLoader类加载一个class时实际上就是通过调用DexPathList.findClass()方法,遍历成员变量DexPathList.Element[] dexElements,如果找到匹配的class那么就直接返回结果,也就是最先被找到的class会被直接使用,不管后面的dex文件里是否还有相同的class。
举个例子:app.apk里有两个dex文件分别是1.dex和2.dex,这两个dex文件都有一个相同com.app.A的class文件,那么BaseDexClassLoader在加载com.app.A的class时,假设1.dex文件对应的Element实例是成员变量数组dexElements的第一个元素,2.dex对应的Element实例是第二个元素,那么通常情况下2.dex里的com.app.A的class永远都不会被加载到。
因此要实现热修复或更新,我们可以把需要修复或更新的类编写好并编译成.class文件后,使用Android sdk提供的dx工具把这些.class文件打包成.dex文件,然后通过使用DexClassLoader手动加载把.dex文件加载进来,上文已提到BaseDexClassLoader在构造时会实例化一个DexPathList类型的成员变量,而这个成变量初始化时也会把.dex文件封装成Element类型并存入DexPathList.Element[] dexElements数组里,然后我们可以通过反射把这个数组和系统默认使用的PathClassLoader里的

本文介绍如何在Android应用中实现Java类的热修复,包括利用DexClassLoader动态加载dex文件及通过反射合并dexElements数组的方法。
最低0.47元/天 解锁文章
3328

被折叠的 条评论
为什么被折叠?



