Android热修复、multi dex入门
学习自https://www.jianshu.com/p/5f390be47ce8
号称最简单的热修复,当然简单也有其弊端,不过作为入门教学,很好
我们需要做的就是把补丁dex放到element数组的第一位。到这里可能还有点模糊,继续。
multi dex
https://blog.****.net/qq_36523667/article/details/80329077这篇文章可以很好的进行一个分包
分包以后的思路讲解
我写了这样一个demo,MainActivity,还有这个类
package com.example.myapplication; /*** * 我们想修复的类,放在分包中 */ public class WantFix { public static String get() { return "这是修复之前的";} }
我把这个类放到了分包classes2.dex中(里面包含的文字是"这是修复之前的"),把MainActivity放到了主包classed.dex中。
然后呢,我会修改这个类的文字为“这是修复之后的”,再进行一次打包。从中获得了最新版的classed2.dex。
然后我会进行一个修复,我会把最新版的classed2.dex动态替换掉原来老的classed2.dex。
现在你懂了为什么分包可以实现热修复了吧!!
再讲底层一点
还是这个图,图中的服务器补丁,dex就是我们这里的最新版的classes2.dex,上面的pathclassloader的element数组中每一个格子都存放了一个dex,我们要做的就是替换掉老版的classes2.dex,实现热修复。
我在修复中遇到的问题
我触发修复的时机太晚了,导致老版的classes2.dex中的类也被加载到内存中了,这样一来,我动态加载新版classes2.dex的时候,又来了个几乎一模一样的类,这样两个类就让我们的DVM糊涂了,我到底该用你们俩的哪个类呢?所以就报错了。正确的时候应该是在Application中进行一个修复,不要在MainActivity中进行一个修复,太晚了,已经触发了老版的类的初始化了。
我在application中修复的代码
package com.example.myapplication; import android.app.Application; import android.content.Context; import android.os.Environment; import java.io.File; import java.lang.reflect.Array; import java.lang.reflect.Field; import dalvik.system.DexClassLoader; public class App extends Application { @Override public void onCreate() { super.onCreate(); try { injectDexElements(this, Environment.getExternalStorageDirectory().getAbsolutePath()+"/classes2.dex"); } catch (Exception e) { e.printStackTrace(); } } public static void injectDexElements(Context context, String patchFilePath) throws Exception { ClassLoader pathClassLoader = context.getClassLoader(); /**dex, 优化后的路径, 必须在要App data目录下, 否则会没有权限*/ File oDexFile = new File(context.getDir("odex", Context.MODE_PRIVATE).getAbsolutePath()); /**dex 补丁文件路径(文件夹)*/ File patchFile = new File(patchFilePath); // if (!patchFile.exists()) { // patchFile.mkdirs(); // } // 合并成一个数组 Object applicationDexElement = getDexElementByClassLoader(pathClassLoader); // for (File dexFile : patchFile.listFiles()) { ClassLoader classLoader = new DexClassLoader(patchFile.getAbsolutePath(),// dexPath oDexFile.getAbsolutePath(),// optimizedDirectory null, pathClassLoader ); // 获取这个classLoader中的Element Object classElement = getDexElementByClassLoader(classLoader); //Log.e("TAG", classElement.toString()); applicationDexElement = combineArray(classElement, applicationDexElement); // } // 注入到pathClassLoader中 injectDexElements(pathClassLoader, applicationDexElement); } /** * 把dexElement注入到已运行classLoader中 * * @param classLoader * @param dexElement * @throws Exception */ private static void injectDexElements(ClassLoader classLoader, Object dexElement) 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); dexElementsField.set(pathList, dexElement); } /** * 合并两个dexElements数组 * * @param arrayLhs * @param arrayRhs * @return */ private static Object combineArray(Object arrayLhs, Object arrayRhs) { Class<?> localClass = arrayLhs.getClass().getComponentType(); int i = Array.getLength(arrayLhs); int j = i + Array.getLength(arrayRhs); Object result = Array.newInstance(localClass, j); for (int k = 0; k < j; ++k) { if (k < i) { Array.set(result, k, Array.get(arrayLhs, k)); } else { Array.set(result, k, Array.get(arrayRhs, k - i)); } } return result; } /** * 获取classLoader中的DexElement * * @param classLoader ClassLoader */ 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; } }
然后我点击了下MainActivity的按钮,发现,卧槽,竟然成功了
喜大普奔。有啥不懂的,欢迎随时留言。