《深入探索Android热修复原理》代码热修复总结

阿里巴巴对热修复技术的发展路线:
1、基于Xposed而来的Dalvik下java method hook技术-Dexposed框架,仅限于Dalvik虚拟机
2、兼容到Art虚拟机的Andfix,同样是基于底层的结构替换方案
3、进而发展就是hotfix,基于Andfix,有所提高,但都没有对资源和so实现修复能力
4、接下来就是这篇主角:17年6月提出的新方案-非入侵时Sophix

代码热修复原理

1、Dalvik下
采用了AndFix中的热修复方法

extern void __attribute__ ((visibility ("hidden"))) dalvik_replaceMethod(
        JNIEnv* env, jobject src, jobject dest) {
    jobject clazz = env->CallObjectMethod(dest, jClassMethod);
    ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef_fnPtr(
            dvmThreadSelf_fnPtr(), clazz);
    clz->status = CLASS_INITIALIZED;

    Method* meth = (Method*) env->FromReflectedMethod(src);
    Method* target = (Method*) env->FromReflectedMethod(dest);
    LOGD("dalvikMethod: %s", meth->name);

    meth->clazz = target->clazz;
    meth->accessFlags |= ACC_PUBLIC;
    meth->methodIndex = target->methodIndex;
    meth->jniArgInfo = target->jniArgInfo;
    meth->registersSize = target->registersSize;
    meth->outsSize = target->outsSize;
    meth->insSize = target->insSize;

    meth->prototype = target->prototype;
    meth->insns = target->insns;
    meth->nativeFunc = target->nativeFunc;
}

直接把java method替换成native method
详细参考:https://www.cnblogs.com/soaringEveryday/p/5338214.html
2、Art下
优化了AndFix下Art虚拟机下native 替换method的方式,通过整体替换ArtMethod而不是替换ArtMethod内部成员变量来达到整体替换,而不会出现因内部结构改变而出现不兼容的情况。如原著作中图示如下:
这里写图片描述

3、热修复方式即时生效局限

1)存在以下两种情况下不能使用

1、类结构发生了变化,包括类的成员函数或成员变量发现了增加和减少情况,均不适用
2、非静态方法在热修复反射调用时会出现新旧类校验导致不适用,只能通过冷启动类替换方式

2)如下情况会导致发生类结构变化
1、内部类编译
2、匿名内部类编译
3、静态域和代码块编译和final static域编译
4、应用混淆导致的方法内联和剪裁
5、switch-case语句编译
6、泛型编译-边际擦除和类型转换
……

冷启动类加载原理

目前主流中大部分都是采用插桩方式-把补丁dex文件通过DexFile.load加载成DexFile对象,作为一个Element插入pathlist中第一位,但是这种方式存在两个问题:

一个是ClASS_ISPREVERIFIED:简单说就是出现了两个相同的类在两个dex文件中,一个补丁dex,一个原dex,原dex在dexopt/dexoat中verify时引用都是在相同dex中而打上这个标签。
另一个是加载时性能问题:正常情况下在安装时候会在dexopt转为成odex时候进行类的Verify和Optimize操作,而插桩方式都绕过了,从而在类加载初始化时候,会重新因为类没有被打上CLASS_ISPREVERIFIED和CLASS_ISOPTIMIED标签而verify和optimize,这时候如果大量的类会导致阻塞主线程。如原著中图示
这里写图片描述

1、Art下方案

在Art虚拟机下已经支持了加载压缩文件中的多个dex文件,而dalvik仅从压缩文件中加载classes.dex文件,Art下优先加载classes.dex,然后加载classes2.dex….,故直接把补丁dex文件命名为classes.dex,原apk中的dex依次重命名为classes2.dex…,然后一起压缩,并通过DexFile.load成DexFile对象,并完成替换dexElements(DexPathlist的成员变量)旧对象,而不是插入到数组的首位。
其中在DexFile.load加载dex文件到native内存之前,会先找对应的odex文件,如果没有,则dalvik下会进行dexopt,Art下会进行dexoat,最后得到一个优化后的odex文件,虚拟机执行的都是odex而不是dex。由于转换成odex过程耗时,阻塞主线程,故原著中建议另起一个线程来转为,若被中断,则删除odex。

扫描二维码关注公众号,回复: 2435454 查看本文章

2、dalvik下全量dex方案

由于dalvik下不具备多dex文件加载,原著提出了一个类似微信Tinker的合成全量dex的方案。由于Tinker中生成补丁dex和合成新dex过于精细,补丁dex是由新老dex比较指令等维度来生成的,而本书中通过在类的维度差役生成补丁,再把原dex中相同的类去掉,再合成一个完整的dex,把补丁插桩到dexElements(DexPathlist的成员变量)数组的第一位,这样先从补丁找类,没有再从原dex中找。其中去掉原dex中的类方法,Sophix提出一种方式如下图:
这里写图片描述
仅仅去掉dex文件中的类的定义,而不是去掉类的实体,这样会大大提搞操作效率和安全性。

3、存在的限制

dvmOptimizeClass优化导致的影响:优化会导致重写虚拟机指令-invoke-virtual 改成invoke-virtual-quick,后面的索引是每个类的vtable中索引值,而这个vtable索引值是按照类中先后顺序而来,就会出现第一位方法错乱。故增加方法都是在类最后增加

猜你喜欢

转载自blog.csdn.net/u010019468/article/details/78890553