Android 热修复

本文探讨了Android热修复的几种方案,包括AndFix通过native层替换Java方法、Robust使用字节码插桩实现实时生效以及Tinker的非及时生效机制,还涉及ClassLoader的工作原理和手动打补丁包的过程。
摘要由CSDN通过智能技术生成

Android 热修复

本文链接:https://blog.csdn.net/feather_wch/article/details/132052856

方案对比

AndFix@Deprecate

1、AndFix为什么可以实时生效?

在native层动态替换掉Java层的方法,通过native层hook java层代码。

2、如何拿到补丁包的有注解Method?

  1. 补丁包包含Test.class
  2. 类加载Test.class
  3. 反射Method
  4. 拿到注解(标记了要替换谁),找到目标类

3、如何完成两个对象的替换?bug method => fix method

Java层伪代码:

BugMethod.clazz = FixMethod.clazz

Native层代码:

replace(env, jobject bug, jobject fix)
{
    // 把bug method的所有属性,都替换为,补丁method的所有属性
}

4、后续拿到的都是修复后的Method.class类对象

Robust-即时生效

美团方案:抖音还在用,纯Java实现

  1. 对每个函数插入一段代码,字节码插桩技术。
  2. 编译阶段,在class字节码写代码
  3. 插入开关: 如果没有补丁包,就返回原先逻辑。有补丁包,交给补丁。
class State{
   public static ChangeQuickRedirect changeQuickRedirect;
   public long getIndex{
    if(changeQuickRedirect != null){
        return PatchProxy.accessDispatch(xxxx);
    }
    return 100;
   }
}
  1. 拿到补丁包内的类对State.changeQuickRedirect进行赋值
Class<StatePatch> clz = StatePatch.class; // 拿到补丁包的类
State.changeQuickRedirect = clz.newInstance(); // 赋值,影响开关的条件判断

相关类:PatchesInfoImpl.java、StatePatch.java

Tinker-非及时生效

1、关键词:DexDiff、增量更新
2、工具:
bsdiff 将两者区别信息,放到patch上

bsdiff 1.txt 2.txt patch
bspatch:合成 1.txt + patch = 2.txt

3、Tinker是差分包 + Bug Dex = 修复后Dex
4、热修复思路 => 将补丁Dex放到数组前面
5、什么时候热修复?越早越好,防止类更早加载,就失效了
6、补丁Dex什么时候删除?不能删。
7、Application有Bug怎么办?不能有Bug

ClassLoader

1、实现类:BootClassLoader、PathClassLoader、DexClassLoader、InMemoryClassLoader

2、下面类的ClassLoader是什么?

MainActivity.class.getClassLoader() // Path 我们
AppCompatActivity.class.getClassLoader() // Path 第三方
Application.class.getClassLoader() // Boot 系统
getClassLoader() // Path 应用

3、PathClassLoader原理

  1. parent => BootClassLoader
public class PathClassLoader extends BaseDexClassLoader {
    public PathClassLoader(String dexPath, ClassLoader parent) {}
    public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {}
}
  1. loadClass做了什么?双亲委派,先交给BootClassLoader去加载。
  2. findClass源码
// BaseDexClassLoader.java
    DexPathList pathList; // 内部有一个dexElements数组,存储着该ClassLoader的所有dex/resources的路径
    public BaseDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent) {
        super(parent);
        this.pathList = new DexPathList(this, dexFiles);// DexPathList
    }
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class c = pathList.findClass(name, suppressedExceptions); // DexPathList中查找
        if (c == null) {
            ClassNotFoundException cnfe = new ClassNotFoundException(
                    "Didn't find class \"" + name + "\" on path: " + pathList);
            throw cnfe;
        }
        return c;
    }
// DexPathList.java
    public Class<?> findClass(String name, List<Throwable> suppressed) {
        for (Element element : dexElements) {
            // Element内部的DexFile中查找
            Class<?> clazz = element.findClass(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }
        return null;
    }
// Element,DexPathList的静态内部类
    private Element[] dexElements;
    static class Element {
        private final File path;
        private final DexFile dexFile;// DexFile
        public Class<?> findClass(String name, ClassLoader definingContext,
                List<Throwable> suppressed) {
            return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                    : null;
        }
    }

实战一

手动打补丁包

1、手动补丁包流程

  1. bug修复
  2. 编译项目,生成类的class文件(make project)
  3. 生成补丁包
dx --dex --output=patch.jar com/zte/tv5gshow/test/Bug.class

2、补丁包放入到DexFile数组

  1. 获取PathClassLoader对象
  2. 反射到DexPathList对象
  3. 反射到Element数组(oldElement)
  4. 把补丁包编程Element数组,patchElement:反射执行makePathElement()
  5. 合并oldElement + patchElement = newElement (Array.newInstance)
  6. 反射吧oldElement数组设置为newElement数组
    makePathElement()需要List File集合
File file = new File("/sdcard/patch.jar")
list.add(file);
// 作为List的参数传入

热修复二

1、PathClassLoader是哪里创建的?

ActivityThread中创建了PathClassLoader并且传入了Apk的dex路径
LoadedApk->ApplicaitonLoaders.getDefault().getClassLoader()
-> ClassLoaderFactory.createClassLoader
-> new PathClassLoader(dexPath, librarySearchPath, parent)

2、so修复 => DexPathList内部属性nativeLibraryPathElements

3、资源修复 => 和换肤一样

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

猎羽

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

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

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

打赏作者

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

抵扣说明:

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

余额充值