IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android热修复学习(一) -> 正文阅读

[移动开发]Android热修复学习(一)

前言

什么是热修复?

当我们应用上线后出现bug需要及时修复时,不用通过新发安装包,而是通过发布补丁包,在客户无感知的情况下修复bug;

热修复执行流程

开发侧修复bug提供补丁包给服务端,服务端将补丁包下发给客户端,客户端拿到补丁包后执行热修复相关代码进行修复;
热修复执行流程图

常用三方热修复框架原理浅析

常见热修复方案比较

热修复方案对比

AndFix 阿里巴巴热修复方案【已停止维护】

github地址

AndFix

方案流程

在native动态替换java层的方法,通过native层hook java层的代码,从而改变执行顺序,进行修复;
AndFix执行流程图

Robust 美团热修复方案

github地址

Robust

方案流程

对每个函数都在编译打包阶段自动的插入一段代码,类似于代理,将方法执行的代码重定向到其他方法中;

   //编写的代码
    @Modify //改动代码后手动添加注解用于补丁包生成
    public long getIndex() {
        return 100;
    }

    //经过插桩后实际执行的代码
    public long getIndex() {
        if (changQuickRedirect != null) {
            return 修复的实现逻辑;
        }
        return 100;
    }

Tinker 腾讯热修复方案

github地址

Tinker
Tinker通过计算比对指定的Base Apk中的dex与修复后的Apk中的dex的区别,补丁包中的内容即为两者差分的描述。运行时将Base Apk中的dex与补丁包进行合成,重启后加载全新合成后的dex文件;
Tinker热修复流程

热修复简单实战

在前面我们学习了插件化相关知识,了解了类加载机制原理以及实现插件化调用,其实热修复插件化实施差不多,区别主要是当进行dex合并的时候,需要将补丁包中的dex加载到base dex的前面,这样才能达到加载补丁包中class的目的。关于类加载机制原理这里就不再赘述,有需要可直接参考:Android插件化学习之初识类加载机制

这里我们简单写个热修复相关的demo;

  • 先写个bug~~
public class TestUtils {
   public static void test() {
       throw new IllegalArgumentException("故意写bug....");
       // Log.e(TestUtils.class.getSimpleName(), "bug 已修复...");
   }
}
  • 将bug修复并打包成dex文件存放到sdcard下
public class TestUtils {
   public static void test() {
      // throw new IllegalArgumentException("故意写bug....");
        Log.e(TestUtils.class.getSimpleName(), "bug 已修复...");
   }
}

使用dx命令打包成dex存放在sdcard目录下;
修复dex存放sdcard下

  • 将修复后的dex与当前dex合并
public class LoadUtils {

   public static void loadPluginDexFile(Context context) {
       try {
           Class<?> baseDexClassLoaderClazz = Class.forName("dalvik.system.BaseDexClassLoader");
           Field pathListField = baseDexClassLoaderClazz.getDeclaredField("pathList");
           pathListField.setAccessible(true);

           Class<?> dexPathListClazz = Class.forName("dalvik.system.DexPathList");
           Field dexElementsField = dexPathListClazz.getDeclaredField("dexElements");
           dexElementsField.setAccessible(true);

           //获取宿主dexElements
           ClassLoader hostClassLoader = context.getClassLoader();
           Object pathList = pathListField.get(hostClassLoader);
           Object[] hostDexElements = (Object[]) dexElementsField.get(pathList);

           //获取插件dexElements
           DexClassLoader pluginDexClassLoader = new DexClassLoader("/sdcard/patch.dex", context.getCacheDir().getAbsolutePath(), null, hostClassLoader);
           Object pluginPathList = pathListField.get(pluginDexClassLoader);
           Object[] pluginDexElements = (Object[]) dexElementsField.get(pluginPathList);

           //合并两个dex Element数组【修复插件的dex放在数组前面】
           Object[] resultDexElements = (Object[]) Array.newInstance(hostDexElements.getClass().getComponentType(), hostDexElements.length + pluginDexElements.length);
           System.arraycopy(pluginDexElements, 0, resultDexElements, 0, pluginDexElements.length);
           System.arraycopy(hostDexElements, 0, resultDexElements, pluginDexElements.length, hostDexElements.length);
           //将合并的结果复制给宿主dex Elements
           dexElementsField.set(pathList, resultDexElements);
       } catch (Exception e) {
           e.printStackTrace();
           Log.e("LoadUtils", e.toString());
       }
   }

Android N混合编译

ART是在Android KitKat(Android4.0)进入并在Lolipop(Android 5.0)中设为默认运行环境,可以看作Dalvik 2.0;
ART模式在Android7.0之前安装APK会采用AOT(Ahead of Time:提前编译、静态编译)预编译为机器码;
而在Android N(Android7.0)使用混合模式的运行时,应用在安装时不做编译,而是运行时解释字节码,同时在JIT编译了一些代码后将这些代码信息记录至Profile文件,等到设备空闲的时候使用AOT(All-Of-the-Time compilation:全时段编译)编译生成称为app_image的base.art(类对象映像)文件,这个art文件会在apk启动时自动加载(相当于缓存)。根据类加载原理,类被加载后无法替换,因此存在无法修复的问题;

混合编译热修复解决方案

运行时,通过反射替换系统创建的PathClassLoader
App image中的class是插入到PathClassLoader中的ClassTable中;假设我们完全废弃掉PathClassLoader,而是采用新建一个PathClassLoader加载后续所有的类,即可达到将缓存数据无用化的目的;具体方式可参考Tinker中的替换方式;

总结

热修复实现原理归根还是类加载机制,与插件化方案的不同之处在于修复dex需要放在合并后的dex数组前面,才能执行修复后的代码;本质上都是对于类加载机制的运用;

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-08-06 10:55:38  更:2022-08-06 11:00:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 4:46:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码