Aandroid最简单最全面的热修复

最近心血来潮想玩玩热修复,于是网上找了一堆文章,捣鼓了好久也没捣鼓出来,因为要么是不全要么就是太过于简单,不适合小白学习,最后经过查找,捣鼓出来了,特记下,以便以后学习:
第一步:新建一个工程项目很在这里插入图片描述很普通的一个工程,页面也很简单就两个按钮在这里插入图片描述
第二步:新建bug类在这里插入图片描述
第三步:写修复工具类,代码如下:
public class FixDexUtil {
private static final String DEX_SUFFIX = “.dex”;
private static final String APK_SUFFIX = “.apk”;
private static final String JAR_SUFFIX = “.jar”;
private static final String ZIP_SUFFIX = “.zip”;
public static final String DEX_DIR = “odex”;
private static final String OPTIMIZE_DEX_DIR = “optimize_dex”;
private static HashSet loadedDex = new HashSet<>();

static {
    loadedDex.clear();
}

/**
 * 加载补丁,使用默认目录:data/data/包名/files/odex
 *
 * @param context
 */
public static void loadFixedDex(Context context) {
    loadFixedDex(context, null);
}

/**
 * 加载补丁
 *
 * @param context       上下文
 * @param patchFilesDir 补丁所在目录
 */
public static void loadFixedDex(Context context, File patchFilesDir) {
    // dex合并之前的dex
    doDexInject(context, loadedDex);
}

/**
 * @author bthvi
 * @time 2018/6/25 0025 15:51
 * @desc 验证是否需要热修复
 */
public static boolean isGoingToFix(@NonNull Context context) {
    boolean canFix = false;
    File externalStorageDirectory = Environment.getExternalStorageDirectory();
    // 遍历所有的修复dex , 因为可能是多个dex修复包
    File fileDir = externalStorageDirectory != null ? new File(externalStorageDirectory, "007") : new File(context.getFilesDir(), DEX_DIR);// data/data/包名/files/odex(这个可以任意位置)
    File[] listFiles = fileDir.listFiles();
    if (listFiles != null) {
        for (File file : listFiles) {
            if (file.getName().endsWith(DEX_SUFFIX) || file.getName().endsWith(APK_SUFFIX) || file.getName().endsWith(JAR_SUFFIX) || file.getName().endsWith(ZIP_SUFFIX)) {
                loadedDex.add(file);// 存入集合
                // 有目标dex文件, 需要修复
                canFix = true;
            }
        }
    } return canFix;
}

private static void doDexInject(Context appContext, HashSet<File> loadedDex) {
    String optimizeDir = appContext.getFilesDir().getAbsolutePath() + File.separator + OPTIMIZE_DEX_DIR;
    // data/data/包名/files/optimize_dex(这个必须是自己程序下的目录)
    File fopt = new File(optimizeDir);
    if (!fopt.exists()) {
        fopt.mkdirs();
    }

    try {
        // 1.加载应用程序dex的Loader
        PathClassLoader pathLoader = (PathClassLoader) appContext.getClassLoader();
        for (File dex : loadedDex) {
            // 2.加载指定的修复的dex文件的Loader
            DexClassLoader dexLoader = new DexClassLoader(
                    dex.getAbsolutePath(),// 修复好的dex(补丁)所在目录
                    fopt.getAbsolutePath(),// 存放dex的解压目录(用于jar、zip、apk格式的补丁)
                    null,// 加载dex时需要的库
                    pathLoader// 父类加载器
            );
            // 3.开始合并
            // 合并的目标是Element[],重新赋值它的值即可
            /**
             * BaseDexClassLoader中有 变量: DexPathList pathList
             * DexPathList中有 变量 Element[] dexElements
             * 依次反射即可
             */
            //3.1 准备好pathList的引用
            Object dexPathList = getPathList(dexLoader);
            Object pathPathList = getPathList(pathLoader);
            //3.2 从pathList中反射出element集合
            Object leftDexElements = getDexElements(dexPathList);
            Object rightDexElements = getDexElements(pathPathList);
            //3.3 合并两个dex数组
            Object dexElements = combineArray(leftDexElements, rightDexElements);
            // 重写给PathList里面的Element[] dexElements;赋值
            Object pathList = getPathList(pathLoader);// 一定要重新获取,不要用pathPathList,会报错
            setField(pathList, pathList.getClass(), "dexElements", dexElements);
        }
        Toast.makeText(appContext, "修复完成", Toast.LENGTH_SHORT).show();
    } catch (Exception e) {
        e.printStackTrace();
    }
}


/**
 * 反射给对象中的属性重新赋值
 */
private static void setField(Object obj, Class<?> cl, String field, Object value) throws NoSuchFieldException, IllegalAccessException {
    Field declaredField = cl.getDeclaredField(field);
    declaredField.setAccessible(true);
    declaredField.set(obj, value);
}

/**
 * 反射得到对象中的属性值
 */
private static Object getField(Object obj, Class<?> cl, String field) throws NoSuchFieldException, IllegalAccessException {
    Field localField = cl.getDeclaredField(field); localField.setAccessible(true); return localField.get(obj);
}
/**
 * 反射得到类加载器中的pathList对象
 */
private static Object getPathList(Object baseDexClassLoader) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
    return getField(baseDexClassLoader, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList");
}
/**
 * 反射得到pathList中的dexElements
 */
private static Object getDexElements(Object pathList) throws NoSuchFieldException, IllegalAccessException {
    return getField(pathList, pathList.getClass(), "dexElements");
}

/**
 * 数组合并
 */

private static Object combineArray(Object arrayLhs, Object arrayRhs) {
    Class<?> clazz = arrayLhs.getClass().getComponentType();
    int i = Array.getLength(arrayLhs);// 得到左数组长度(补丁数组)
    int j = Array.getLength(arrayRhs);// 得到原dex数组长度
    int k = i + j;// 得到总数组长度(补丁数组+原dex数组)
    Object result = Array.newInstance(clazz, k);// 创建一个类型为clazz,长度为k的新数组
    System.arraycopy(arrayLhs, 0, result, 0, i);
    System.arraycopy(arrayRhs, 0, result, i, j);
    return result;
}

}
第四步:MainActivity两个按钮操作也贴出来吧:
在这里插入图片描述
init()方法如下: File externalStorageDirectory = Environment.getExternalStorageDirectory();
// 遍历所有的修复dex , 因为可能是多个dex修复包
// data/user/0/包名/files/odex(这个可以任意位置)
File fileDir = externalStorageDirectory != null ? new File(externalStorageDirectory, “007”) : new File(getFilesDir(), FixDexUtil.DEX_DIR);
if (!fileDir.exists()) {
fileDir.mkdirs();
}
if (FixDexUtil.isGoingToFix(this)) {
FixDexUtil.loadFixedDex(this,
Environment.getExternalStorageDirectory());
}
此刻运行项目点击按钮bug,项目铁定闪退的。
接下来就是修复了,用心看了,不然一不下心就修复不了的:
一、修改bug类
在这里插入图片描述
二、修改工程的安装:
在这里插入图片描述
三、Build→Rebuild Project
四、找到在这里插入图片描述第五步:最关键的一步了 ,因为要把class转换成dex
首先新建一个文件夹(任意位置都可以)
其次在新建文件夹在这里插入图片描述一定要和上面的位置相对应不然打不出dex文件
最后了 打开命令 开始键+R →输入 cmd 一定要先进到sdk的这个文件下(其他的也行)在这里插入图片描述输入命令dx --dex --output=D:\dex\out.dex D:\dex ; D:\dex\out.dex是指输出到哪个文件夹下叫什么文件 在这里插入图片描述看有了吧
奇迹就在这了在这里插入图片描述 看到没 把打出的dex文件复制到这个文件夹下 然后重新打开程序 点击修复 再点击bug按钮 程序奇迹般的出来结果了。。。。。 本人小白 写的不好 如有问题请留言 大家一起交流 2622760038