阿里巴巴对热修复技术的发展路线:
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。

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索引值是按照类中先后顺序而来,就会出现第一位方法错乱。故增加方法都是在类最后增加

更多相关文章

  1. Android(安卓)drawable文件夹的使用
  2. Android中实现对/system/bin/surfaceflinger进程进行拦截和注入
  3. Maven系列 1. Maven入门
  4. Android放置图片的三个文件夹
  5. Android中Lottie的简单使用
  6. 1.Android查看手机内部储存目录及数据库文件之通过模拟器Monitor
  7. android 自定义手势
  8. [Android(安卓)API学习]Data Storage胡乱翻译(2)
  9. Android应用程序插件化研究之DexClassLoader

随机推荐

  1. 每天一算: Number of Boomerangs
  2. 使用Azure Backup还原云端VM
  3. 二叉树实现
  4. linux文件目录结构汇总!Linux学习
  5. 使用pkg打包编译nodejs程序,手动设置缓存
  6. 来了!Python官方文档中文版
  7. 使用AndroidStudio生成打有系统签名的apk
  8. 来了!Django 2.2 正式发布
  9. 添加到我的小程序动画实现详细讲解,轻松学
  10. 一份来自 StackOverflow 的最佳 Python