深入探索安卓热修复技术-观后感

本文深入探讨热修复技术的各种实现方案,包括底层替换、类加载、代码插桩等,对比不同方案的优缺点,如实时生效、兼容性及包大小的影响。同时,分析资源修复和SO热修复的机制,以及Sophix、Tinker等热门工具的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代码修复:

  1. 底层替换方案:实时生效、兼容性差(厂商可以改动Method结构体)、限制条件多(不能对原有类进行方法和字段的增减,会改变字段和方法得索引,如果是增加,还会存在访问不到新增字段的问题)
    • 支付宝 AndFix:底层结构替换、运行时生效、Dalvik和ART全兼容
    • 阿里百川 HotFix:逻辑解耦版
  2. 类加载方案:冷启动生效(在App运行到一半的时候,所有需要发生变更的类已经被加载过了,在Android上是无法对一个类进行卸载的)、兼容性好
    • 新增DEX:需要解决PreVerify问题,Dalvik下应用启动速度变慢(Art虚拟机采用了Aot模式,在应用安装时便生成好了机器码,关键数据结构:class_index、method_index,Dalvik在安装时会生成odex文件,但不会进一步生成字节码,而是在每次应用启动加载类时生成对应的字节码,如果没有打上preverify标记,则每次生成机器码是都会进行verify、optimize操作)、另外Art模式下地址写死,如果Patch类新增了方法,那么必须包含Patch的引用类,导致Patch包体积过大(Art虚拟机在当前Dex对应的Index表中找不到对应方法时,会执行Trampoline蹦床指令,用于从Native Code切换到虚拟机
    • 合并DEX:复杂度高、不存在PreVerify问题、不影响应用启动速度(Tinker在方法指令维度进行合成,复杂度高、包体积小;Sophix在类维度进行合成、时间空间均衡)
  3. 代码插桩方案:实时生效、兼容性好、增加包大小
    • 美团Robust
  4. 底层替换方案限制条件:
    1. 引起原有类发生结构变化的修改(方法中访问内部类或者外部类的私有成员会新增access方法、方法中增加或减少内部类会导致类名混乱、方法内联、方法参数裁剪),方法内联和方法裁剪可以通过-dontoptimize关掉(android sdk默认的混淆配置文件proguard-android-optimize.txt和proguard-android.txt,这两者的区别就是后者应用了-dontoptimize选项
    2. 修复的非静态方法会被反射调用

资源修复:

  1. Instant Run:构造全部资源的AssetManager,全量替换AssetManager引用
  2. 主流热修复:Hook AAPT任务,修改Package Id解决资源冲突(热修复包只需包含新增资源,不需要全量替换AssetManager引用)

SO热修复:

//TODO

Sophix(集大成者):

  1. 代码修复:
    1. 底层替换方案:整体替换Method结构(Method结构体线性排列,连续两个静态方法的地址相减),解决机型兼容问题
    2. 类加载方案:类层级的Dex Merge,复杂度低,解决PreVerify问题

知识点:

范型方法:类型擦除(Object参数)和多态的冲突(通过桥接方法来解决:覆写方法里面调用真正的子类方法)

Tinker原理:

  1. Dex Merge示意图:https://blog.youkuaiyun.com/qq_22393017/article/details/73999210
  2. Tinker在合并完成后,启动应用时,先将放在本地目录下合成后的新dex文件读取出来,然后通过反射获取应用ClassLoader的 dexElements 数组,并将新的dex给合并进去。由于完全使用了新的Dex,在Dalvik环境下无须通过插桩来避免类被打上CLASS_ISPREVERIFYED ,因为Tinker合成后的补丁类依旧在原来的dex中,而不是像Qzone那样把补丁类单独放到一个dex去
  3. 混合编译问题:运行时生成新的PathClassLoader,并全量替换老的PathClassLoader
    1. 创建新Loader的流程:替换老loader的pathList对象的definingContext属性,然后把老loader的pathList赋值给新的loader、反射老loader的pathList的makePathElements方法并调用,注意第二个方法参数设置为null,重新生成dexElements数组,并替换原来的数组,最终完成AndroidNClassLoader的创建
    2. 替换新Loader的流程:在全局Context中持有的LoadedApk的对象mPackageInfo的属性中,有一个ClassLoader类的对象mClassLoader,替换掉。同时将Thread中持有的ClassLoader也同步替换为AndroidNClassLoader
  4. Aot关键方法:
  5. if (useInterpretMode) {
        interpretDex2Oat(dexFile.getAbsolutePath(), optimizedPath);
    } else if (Build.VERSION.SDK_INT >= 28 || (Build.VERSION.SDK_INT >= 27 && Build.VERSION.PREVIEW_SDK_INT != 0)) {
        AndroidNClassLoader.triggerDex2Oat(context, dexFile.getAbsolutePath());
    } else {
        DexFile.loadDex(dexFile.getAbsolutePath(), optimizedPath, 0);
    }
    
    public static void triggerDex2Oat(Context context, String dexPath) {
        final ClassLoader bootClassLoader = Context.class.getClassLoader();
        // Suggestion from Huawei: Only PathClassLoader (Perhaps other ClassLoaders known by system
        // like DexClassLoader also work ?) can be used here to trigger dex2oat so that JIT
        // mechanism can participate in runtime Dex optimization.
        new PathClassLoader(dexPath, bootClassLoader);
    }
  6. 详细原理参考:https://blog.youkuaiyun.com/l2show/article/details/53307523、https://www.jianshu.com/p/aff8f887a49f
  7. 知识点:
    1. DexClassLoader和PathClassLoader的区别:https://blog.youkuaiyun.com/mynameishuangshuai/article/details/52737581

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

little-sparrow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值