热修复比较

热修复框架种类和对比

1.资源修复有2种方案

a.instantRun方案

丶通过反射创建一个新的assetManager

丶通过反射调用addAssetPath方法加载外部的资源

丶通过反射修复每一个activity的Resource中的mAssets字段(所有引用到地方都改)

b.SoPhix方案

这个是 Sophix 采用的方案,原理是构造一个 package id 为 0x66 的资源包,只含有改变的资源,将其直接添加到原有的 AssetManager 中,这样不会与原来的 package id 0x7f 冲突。然后将原来的 AssetManager 重新进行初始化即可,就不需要进行繁琐的反射替换操作了。

2. 代码修复有3种方案

a.类加载方案,看下DexPathList的findclass方法

public Class<?> findClass(String name, List<Throwable> suppressed) {
        for (Element element : dexElements) {
            Class<?> clazz = element.findClass(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }

        if (dexElementsSuppressedExceptions != null) {
            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
        }
        return null;
    }

一个element实际就代表一个dex文件,找一个类,是从第一个dex对应的element里面的dexfile去找

从上面的代码就得到一种方案,用PathClassloader或者DexClassloader加载一个外部dex

然后把他的element放在原本的elements之前,这样以后就会加载新的dex中的类了。

类加载方案是需要重启App的,因为类无法卸载,要重新加载类,就必须重启app

虽然很多热修复框架都是采用类加载方案,但是还是有一些区别。

a.QQ空间超级补丁和Nuwa是上面的降补丁包放在elements数组第一个元素

b.微信tinker是新旧apk做diff,得到path.dex再与手机原本的dex合并,生成新的class.dex,然后再反射将此dex放在第一个元素

c.饿了么的Amigo是将每一个element取出来,组成新的element数组,运行时再用新的element数组替换现有的数组

b.底层替换方案,nativehook

底层替换方法,不会从新加载类,限制较多,不能增减原有类的方法和字段,是及时生效的

原理是直接在native层替换方法入口,或者整个方法体

c.instantRun方案

原理是每个类都增加一个静态变量,在每个方法执行前,判断此变量是否发生变化,变化了执行新的类中方法

参考这种方法的有美团Robust和Aceso

动态链接库修复

1.替换System.loadLibrary (tinker方式)

在加载 so 库的时候,系统提供了两个接口

System.loadLibrary(String libName):用来加载已经安装的 apk 中的 so

System.load(String pathName):可以加载自定义路径下的 so

通过上面两个方法,我们可以想到,如果有补丁 so 下发,我们就调用 System.load 去加载,如果没有补丁 so 没有下发,那么还是调用 System.loadLibrary 去加载系统目录下的 so,原理比较简单,但是我们需要再上面进行一层封装,并对调用 System.loadLibrary 的地方都进行替换。

2.修改nativeLibraryPathElments的值

与类加载修复一样,获取新的so路径,通过反射将它设置在此elements之前,当找到path之后就会return。不会去找后面的path了。

DexPathList的findLibrary方法

 public String findLibrary(String libraryName) {
        String fileName = System.mapLibraryName(libraryName);

        for (NativeLibraryElement element : nativeLibraryPathElements) {
            String path = element.findNativeLibrary(fileName);

            if (path != null) {
                return path;
            }
        }

        return null;
    }

可以参考部分以下代码

Field pathListField = ShareReflectUtil.findField(classLoader, "pathList");
Object dexPathList = pathListField.get(classLoader);
Field nativeLibraryPathElements = ShareReflectUtil.findField(dexPathList, "nativeLibraryPathElements");
nativeLibraryPathElements.setAccessible(true);
nativeLibraryPathElements.set(dexPathList, nnnnnewelements);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值