我们在服务端下载修复bug后的classes.dex并且动态替换原有的classes.dex这个过程的源代码的分析是我们今天要讨论的。
文末给出所有源码。
Context家族的classloader就是Android默认的加载器PathClassLoader。所以我们很简单一句话就可以获得了。
ClassLoader pathClassLoader = context.getClassLoader();
PathClassLoader只能加载已安装的apk,dex class loader则没有这个限制。所以我们需要创建一个dex class loader
ClassLoader classLoader = new DexClassLoader(patchFile.getAbsolutePath(),// dexPath oDexFile.getAbsolutePath(),// optimizedDirectory null, pathClassLoader );
oDexFile.getAbsolutePath()是odex的路径,他相当于是classes.dex的helper,我们约定俗成在创建dex class loader需要传入他的路径。
patchFile.getAbsolutePath(),他就是我们的服务端传来的用于替换原来bug的dex的dex了。
这样声明以后,这个新的dex中所有的类,我们都可以通过dex class loader去访问了哦!!
然后我们就是分别获取PathClassLoader和dex class loader的element数组,然后把dex class loader的element插入到前者的某一位上即可。PathClassLoader中每一位都是一个dex,如果你不分包的话,他只有一个dex,所以我们想要热修复,自然是需要分包的。
我们看一下从classloader中获取element数组的方法
private static Object getDexElementByClassLoader(ClassLoader classLoader) throws Exception { Class<?> classLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader"); Field pathListField = classLoaderClass.getDeclaredField("pathList"); pathListField.setAccessible(true); Object pathList = pathListField.get(classLoader); Class<?> pathListClass = pathList.getClass(); Field dexElementsField = pathListClass.getDeclaredField("dexElements"); dexElementsField.setAccessible(true); Object dexElements = dexElementsField.get(pathList); return dexElements; }
我们用反射去拿到了classloader中的pathlist,再用反射去拿到pathlist中的element数组,就这么简单
我们看一下我们的dex class loader的element数组是怎么插入到前者的
private static Object combineArray(Object arrayLhs, Object arrayRhs) { Class<?> localClass = arrayLhs.getClass().getComponentType(); int i = Array.getLength(arrayLhs);