Sophix相关书籍
深入探索Android热修复技术原理这本书主要讲解了Android的热修复中的热部署,冷部署以及资源和so库的修复技巧。全文主要讲Sophix应对以上四个方面的技术解析,不管是自家产品还是业界其他方案的横纵对比,Sophix技术目前都是最优的。
- 补丁小,合成不占太多空间和性能。
- 对代码的侵入小,对native代码的hook也精简,做到最大兼容。
- 支持的修复范围广。支持小范围的即时生效和大范围的冷启动。也支持so库和资源修复。
Hook技术
在事件分发流中,通过Hook钩子在事件传送到终点前截获并监控事件的传输,从而处理一些特定干预事件。
- Java API Hook
通过对Android平台的虚拟机注入与Java反射的方式,来改变Android虚拟机调用函数的方式(ClassLoader),从而达到Java函数重定向的目的。参考
Sophix不同之处
Sophix同时使用了热启动的底层替换方案及冷启动的类加载方案,两个方案使用的补丁是相同的。优先热启动。
代码修复方案
底层替换方案
- 原理:在已经加载的类中直接替换掉原有方法,是在原有类的结构基础上进行修改的。在hook方法入口ArtMethod时,通过构造一个新的ArtMethod实现替换方法入口的跳转。
- 应用:能即时生效,Andfix采用此方案。
- 缺点:底层替换稳定性不好,适用范围存在限制,通过改造代码绕过限制既不优雅也不方便,并且还没提供资源及so的修复。
类加载方案
原理:让app重新启动后让ClassLoader去加载新的类。如果不重启,原来的类还在虚拟机中无法重复加载。
优点:修复范围广,限制少。
应用:腾讯系包括QQ空间,手QFix,Tinker采用此方案。
QQ空间会侵入打包流程。
QFix需要获取底层虚拟机的函数,不稳定。
Tinker是完整的全量dex加载。
dex的大小占整个apk比例较低,一个app里的dex文件大小不是主要部分,占空间大的主要是资源文件。
QQ空间的插桩原理
将一个单独无关版主类放到一个单独的dex中,原dex中所有类的构造函数都引用这个类,一般的实现方法都是侵入dex打包流程,利用.class字节码修改技术,在所有.class文件的构造函数中引用这个帮助类。-
Tinker与Sophix方案不同之处
Tinker采用dex merge生成全量DEX方案。反编译为smali,然后新apk跟基线apk进行差异对比,最后得到补丁包。
Dalvik下Sophix和Tinker相同,在Art下,Sophix不需要做dex merge,因为Art下本质上虚拟机已经支持多dex的加载,要做的仅仅是把补丁dex作为主dex(classes.dex)加载而已:
将补丁dex命名为classes.dex,原apk中的dex依次命名为classes(2, 3, 4...).dex就好了,然后一起打包为一个压缩文件。然后DexFile.loadDex得到DexFile对象,最后把该DexFile对象整个替换旧的dexElements数组就好了。
DexFile.loadDex流程
DexFile.loadDex尝试把一个dex文件解析并加载到native内存,在加载到native内存之前,如果dex不存在对应的odex,那么Dalvik下会执行dexopt,Art下会执行dexoat,最后得到的都是一个优化后的odex。实际上最后虚拟机上执行的是这个odex而不是dex。
资源修复方案
基本参考InstantRun的实现:构造一个包含所有新资源的新的AssetManager。并在所有之前引用到原来的AssetManager通过反射替换掉。
Sophix不修改AssetManager的引用,构造的补丁包中只包含有新增或有修改变动的资源,在原AssetManager中addAssetPath这个包就可以了。资源包不需要在运行时合成完整包。
so库修复方案
本质是对native方法的修复和替换。类似类修复反射注入方式,将补丁so库的路径插入到nativeLibraryDirectories数据最前面。
热修复主流框架对比可查阅
Android热修复主流框架调研