实现Andfix热更新

作者: 程序员阿兵 | 来源:发表于2019-06-03 14:45 被阅读87次

一.什么是热修复:

正常开发流程

image.png

热修复开发流程

image.png

热修复优势

image.png

Andfix的原理是,抽象类加载器classLoader的子类BaseDexClassLoader初始化pathList,pathList这个对象中存储着含.dex的dexElements数组,线上出现问题,提供将修复的类包装成.dex文件加入dexElements数组中前列,通过反射合并插桩pathList,最终修改了pathList中的dexElements数组对象。

下面演示编写bug,在线修复。

点击按钮 :10/0
public class ParamsSort {

    public void math(Context context) {
        int a = 10;
        int b = 1;
        Toast.makeText(context, "math >>> " + a / b, Toast.LENGTH_SHORT).show();
    }
}


1.通过服务端接口下载dex文件,拷贝到私有目录临时文件夹 odex

// classes2.dex ---> /storage/emulated/0/classes2.dex
    private void fixBug() {
        // 通过服务器接口下载dex文件,v1.3.3版本有某一个热修复dex包
        File sourceFile = new File(Environment.getExternalStorageDirectory(), Constants.DEX_NAME);

        // 目标路径:私有目录里的临时文件夹odex
        File targetFile = new File(getDir(Constants.DEX_DIR, Context.MODE_PRIVATE).getAbsolutePath()
                + File.separator + Constants.DEX_NAME);

        // 如果存在,比如之前修复过classes2.dex。清理
        if (targetFile.exists()) {
            targetFile.delete();
            Toast.makeText(this, "删除已存在的dex文件", Toast.LENGTH_SHORT).show();
        }

        try {
            // 复制修复包dex文件到app私有目录
            FileUitls.copyFile(sourceFile, targetFile);
            Toast.makeText(this, "复制dex文件完成", Toast.LENGTH_SHORT).show();
            // 加载热修复Dex文件
            FixDexUtils.loadFixedDex(this);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

2.拷贝完成后将需要修复的dex集合存入,主包情况不加入插桩队列:

 /**
     * 加载热修复的dex文件
     * @param context 上下文
     */
    public static void    loadFixedDex(Context context) {
        if (context == null) return;
        // Dex文件目录(私有目录中,存在之前已经复制过来的修复包)
        File fileDir = context.getDir(Constants.DEX_DIR, Context.MODE_PRIVATE);
        File[] listFiles = fileDir.listFiles();
        // 遍历私有目录中所有的文件
        for (File file : listFiles) {
            // 找到修复包,加入到集合
            if (file.getName().endsWith(Constants.DEX_SUFFIX) && !"classes.dex".equals(file.getName())) {
                loadedDex.add(file);
            }
        }

        // 模拟类加载器
        createDexClassLoader(context, fileDir);
    }

3.创建加载补丁的DexClassLoader

  private static void createDexClassLoader(Context context, File fileDir) {
        // 创建临时的解压目录(先解压到该目录,再加载java)
        String optimizedDir = fileDir.getAbsolutePath() + File.separator + "opt_dex";
        // 不存在就创建
        File fopt = new File(optimizedDir);
        if (!fopt.exists()) {
            // 创建多级目录
            fopt.mkdirs();
        }
        for (File dex : loadedDex) {
            // 每遍历一个要修复的dex文件,就需要插桩一次
            DexClassLoader classLoader = new DexClassLoader(dex.getAbsolutePath(),
                    optimizedDir, null, context.getClassLoader());
            hotfix(classLoader, context);
        }
    }

4.热修复:


    /**
     * 热修复
     * @param classLoader 自有的类加载器,加载了修复包的DexClassLoader
     * @param context 上下文
     */
    private static void hotfix(DexClassLoader classLoader, Context context) {
        // 获取系统PathClassLoader类加载器
        PathClassLoader pathLoader = (PathClassLoader) context.getClassLoader();

        try {
            // 获取自有的dexElements数组对象
            Object myDexElements = ReflectUtils.getDexElements(ReflectUtils.getPathList(classLoader));

            // 获取系统的dexElements数组对象
            Object systemDexElements = ReflectUtils.getDexElements(ReflectUtils.getPathList(pathLoader));

            // 合并成新的dexElements数组对象
            Object dexElements = ArrayUtils.combineArray(myDexElements, systemDexElements);

            // 通过反射再去获取   系统的pathList对象
            Object systemPathList = ReflectUtils.getPathList(pathLoader);

            // 重新赋值给系统的pathList属性  --- 修改了pathList中的dexElements数组对象
            ReflectUtils.setField(systemPathList, systemPathList.getClass(), dexElements);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

上面通过反射获取系统dexElements 和修复的dexElements然后合并数组,再通过反射获取pathList 去重新付值dexElements

public class ReflectUtils {

    /**
     * 通过反射获取某对象,并设置私有可访问
     *
     * @param obj   该属性所属类的对象
     * @param clazz 该属性所属类
     * @param field 属性名
     * @return 该属性对象
     */
    private static Object getField(Object obj, Class<?> clazz, String field)
            throws NoSuchFieldException, IllegalAccessException, IllegalArgumentException {
        Field localField = clazz.getDeclaredField(field);
        localField.setAccessible(true);
        return localField.get(obj);
    }

    /**
     * 给某属性赋值,并设置私有可访问
     *
     * @param obj   该属性所属类的对象
     * @param clazz 该属性所属类
     * @param value 值
     */
    public static void setField(Object obj, Class<?> clazz, Object value)
            throws NoSuchFieldException, IllegalAccessException, IllegalArgumentException {
        Field localField = clazz.getDeclaredField("dexElements");
        localField.setAccessible(true);
        localField.set(obj, value);
    }

    /**
     * 通过反射获取BaseDexClassLoader对象中的PathList对象
     *
     * @param baseDexClassLoader BaseDexClassLoader对象
     * @return PathList对象
     */
    public static Object getPathList(Object baseDexClassLoader)
            throws NoSuchFieldException, IllegalAccessException, IllegalArgumentException, ClassNotFoundException {
        return getField(baseDexClassLoader, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList");
    }

    /**
     * 通过反射获取BaseDexClassLoader对象中的PathList对象,再获取dexElements对象
     *
     * @param paramObject PathList对象
     * @return dexElements对象
     */
    public static Object getDexElements(Object paramObject)
            throws NoSuchFieldException, IllegalAccessException, IllegalArgumentException {
        return getField(paramObject, paramObject.getClass(), "dexElements");
    }
}

相关文章

  • 实现Andfix热更新

    一.什么是热修复: 正常开发流程 热修复开发流程 热修复优势 Andfix的原理是,抽象类加载器classLoad...

  • Android_Andfix兼容和Sophix简单分析

    Andfix基础实现请见Android_热修复_Andfix原理分析 前言 Andfix存在版本兼容问题,已停止更...

  • Android热更新 — Andfix

    如需转载请评论或简信,并注明出处,未经允许不得转载 写在前面 最近公司有一个新项目,在开始项目之前需要调研一些热更...

  • android热修复平台架构方案

    热修复基于阿里的andfix实现,在原更新平台上添加新功能模块来实现。 1、系统结构 (1)整体结构 保持不变 (...

  • Android 主要的热修复方案原理分析

    目前较为成熟的热修复框架主要有AndFix、Nuwa以及微信的热更新思想。现在将其主要思想总结如下: AndFix...

  • AndFix热修复原理分析与手写实现

    什么是AndFix? AndFix是阿里推出的热修复框架,热修复是针对线上的出现的轻量级bug,在不进行版本更新的...

  • Android热更新八:AndFix热更新原理

    很早之前就想深入的研究和学习一下热修复,由于时间的原因一直拖着,现在才执笔弄起来。 Android而更新系列:An...

  • Android热更新之Nuwa实践

    今天给大家讲一下热更新之Nuwa实践。 热更新的框架有很多,比如AndFix、HotFix、Dexposed以及N...

  • 各大企业面试笔记

    德丰集团面试: 1.热更新的实现原理 andfix直接在native层替换原有的方法。由于底层替换原理只支持方法替...

  • andfix使用说明

    andfix的主要功能是完成热更新,可以在不用下载新的apk的情况下完成对应用程序的更新。Andfix的基本流程图...

网友评论

    本文标题:实现Andfix热更新

    本文链接:https://www.haomeiwen.com/subject/cwmqxctx.html