Android热修复、multi dex入门

学习自https://www.jianshu.com/p/5f390be47ce8

号称最简单的热修复,当然简单也有其弊端,不过作为入门教学,很好





我们需要做的就是把补丁dex放到element数组的第一位。到这里可能还有点模糊,继续。


multi dex

https://blog.csdn.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的按钮,发现,卧槽,竟然成功了



喜大普奔。有啥不懂的,欢迎随时留言。

猜你喜欢

转载自blog.csdn.net/qq_36523667/article/details/80324341