Comments (1)
sophix热修复技术:
Android修复一般分三步,代码热修复、资源热修复、so包热修复,阿里经过研究业界的各种修复技术,最终出版了sophix技术,代码修复结合了热修复和冷启动两种修复方式,资源热修复采用了重命名资源的packageId,so库采用重命名,让so库提前加载,整体性能比现在业界的各种修复方案效果都好,具体使用手册可参考[管理后台使用手册]https://help.aliyun.com/document_detail/51434.html?spm=5176.doc53287.6.552.vZxNDm
下面是本人结合《深入探索Android热修复技术原理》一书的学习笔记,没有原理性的东西,主要是研究它的技术点。
1. 代码热修复
1.1 传统两大方案:
(1) 底层替换方案:在已加载的类中直接替换掉原有方法,在原来类基础上修改;
优点:时效性好,加载轻快,立即见效;
缺点:无法实现对原有类进行方法和字段增减,这样将破坏原有类结构;兼容性差;
市场上已经的修复方案:
Dexposed, andfix或其他Hook方案,直接依赖修改虚拟机实现的具体字段,如改Dalvi方法的jni函数指针、类或方法访问权限等,它们都是基于Android开源代码改的,如果厂商把ArtMethod类结构修改,则无法兼容。
(2) 类加载方案:在app重启后让Classloader去加载新的类。
优点:修复范围广,限制少;
缺点:时效性差,需要冷启动才能见效;
一些修复方案:
- QQ空间:采用插桩方式解决Dalvik下unexpected dex problem问题,单独放一个帮助类在独立的dex中让其他类调用,防止类被打上CLASS_ISPREVERIFIED标志。加载补丁dex得到dexFile对象,构建一个Element对象插入到dexElements数组的最前面。
优点:没有合成包,产物小,灵活;
缺点:侵入打包流程,为了hack添加一些无用信息,实现起来不优雅;Dalvik下影响类加载性能,Art下类地址写死,导致必包含父类/引用,导致补丁包大。
总结:插桩导致所有类都非preverify,这导致verify和optimize操作都会在加载类时触发,类加载有一定的性能损耗。 - QFix:需要获取底层虚拟机的函数,不够稳定可靠,且无法新增public函数;
- Tinker:提供dex差量包,将patch.dex与应用的class.dex合并成一个完整的dex,得到dexFile对象作为参数,构建一个Element对象,再整体替换旧的dexElements数组。
优点:补丁包小,dex merge成完整的dex,Dalvik不影响加载性能,Art下也不存在必须包含父类/引用类的情况;
缺点:dex合并内存消耗在vm heap上,容易OOM,导致dex合并失败。
总结:Tinker方案:完整的全量dex加载,从dex折方法和指令维度进行全量合成,比较粒度过细,实现复杂,性能消耗严重。
1.2 sophix采用的方案
sophix将两种方案结合,在此两种方案基础上进行改进的方案:
在生成补丁联合体,补丁工具会根据实据代码变动情况自动选反地,小修改,在底层修改方案限制范围内的,直接采用底层替换修复,做到代码修改即时生效;
超过底层替换限制的代码修改,使用类加载替换,冷启动修复;
(1) sophix底层修复改进方案:忽略底层ArtMethod结构差异,对所有Android版本都不需要区分,统一以memcpy实现,代码量减少,且只要保证ArtMethod数组仍是线性结构,就能适用Android各大版本。
(2) sophix类替换方案改进:基线包dex里去掉补丁包中的class,原先需要发生变更的旧class被消除,基线包dex里只包含不变的class.而修改的class要用到补丁中的新class时会自动找到补西dex。对于从原dex中去掉补丁包中class,只需要在解析这个dex时找不到class定义即可,只需移除定义的入口,对于class的具体内容不进行删除,这样可最大可能减少offset的修改。
sophix在Art下实现冷启动
Art下支持加载压缩文件中包含的多个dex,优先加载主dex(classes.dex),后续加载其他dex。所以解决办法是将补丁类放到classes.dex中,原apk中的dex依次命名为classes(2, 3, 4,...).dex,再一起打包成一个压缩文件,所续出现在其他dex类的补丁类是不会被重复加载。DexFile.loadDex得到DexFile对象,最后把该DexFile对象整体替换旧的dexElements数组就可以。
2. 资源热修复
Instant Run资源热修复分两步:
- 构造一个新的Assetmanager,通过反射调用addAssetPath, 把这个完整的资源包加入到AssetManager中,得到一个含有所有新资源的AssetManager;
- 找到所有之前引用的原有AssetManager的地方,通过反射,把引用处替换为含有新资源的AssetManager;
sophix的方案:构造一个package id为0x66的资源包,这个包中只包含改变的资源项,直接在原AssetManager中addAssetPath这个包就可以了。
3. so库修复
把补丁so库的路径插入到nativeLibraryDirectories数组的最前面,就能达到加载so库时是补丁so库,而非原来so库的目录。
4. 其他知识
4.1 android虚拟机加载dex的过程
加载一个dex文件到本地内存时,若不存在odex文件,会先执行dexopt,最后调用verifyAndOptimizeClass执行verify/optimize操作。
(1) dvmVerfiClass:类校验,防止类被篡改校验类的合法性。会对类的每个方法进行校验,如果类的所有方法中直接引用到的类和当前类在同一个dex中,dvmVerifyClass就返回true,此dex中的类就会被打上CLASS_ISPREVERIFIED标志。
(2) dvmOptimizeClass: 类优化,把部分指令优化成虚拟机内部指令,如方法调用指令:invoke- *指令变成了invoke- * -quick,加快方法执行速率,类被打上CLASS_ISOPTIMIZED标志。
4.2 类加载过程中:会依次调用resolve -> link -> init
(1) dvmResolveClass: 类解析,确认方法能正常被调用,如果类被打上了CLASS_ISPREVERIFIED标志,而被调用的类却和它不在同一个dex中,会抛出dvmThrowIllegalAccessError。为了解决此问题,QQ空间采用将一个单独帮助类放到一个单独的dex中,原dex所有类的构造函数都引用这个类(侵入dex打包流程,利用.class字节码修改技术,在所有.class文件构造函数中引用这个帮助类),这就是所谓的插桩实现。使得dexopt过程中dvmVerifyClass类校验近回false,原dex中所有类都没有CLASS_ISPREVERIFIED标志,解决运行时这个异常。
(2) dvmLinkClass: 父类/实现接口权限检查。
(3) dvmInitClass: 类解析完毕后对类进行初始化操作,完成父类和当前类、static变量的初始化等操作;如果类没有被打上CLASS_ISPEVERIFIED标志,会再次进行dvmVerfiClass类校验,若没有打上CLASS_ISOPTIMIZED标志,会再次进行类Optimize操作。正常情况下verify和optimize都仅是在apk第一次安装执行dexopt,而由于为了解决调用补丁包中方法报错,QQ空间采用的插桩方法,会使类的加载效率带来比较大的影响后果。
4.3 内部类编译
内部类编译:内部类会在编译期被编译为跟外部类一样的顶级类,非静态内部类,编译期间自动合成this$0域表示外部类的引用,非静态内部类会持有外部类的引用,静态内部类不持有外部类的引用。外部类为访问内部类私有域/方法,编译期间会为内部类生成access&**相关方法。
匿名内部类编译:匿名内部类就是没有名字的类,编译后类名一般是: 外部类&number,后面的number是编译期根据匿名内部类在外部类中出现的先后关系,依次累加命名的。
4.4 方法编译
如果混淆配置文件中添加上了-dontoptimize这项就不会做方法裁剪和内联,但是没有加则会进行。
如果应用了混淆,可能导致方法的内联和裁剪,有可能导致method的新增/减少。
方法内联发生的情况:
- 方法没有被其他任何地方用到,该方法会被内联到;
- 方法足够简单,如一个方法实现只有一行,该方法会被内联到,任何调用该方法的地方都会被该方法实现替换掉;
- 方法只被一个地方引用到,这个地方会被方法的实现替换掉;
方法裁剪:如果方法中参数未被使用,则可能在编译期间将该参数忽略掉,变成无参函数
4.5 有关混淆
input.jars --shrink--> shrunk code --optimize--> optimized code --obfuscate--> obfucated code --preverify--> output.jars
- shrink:查找工程中类/类成员是否被使用,若未被使用,将其移除。
- optimize: 进一步优化代码,不是入口点的类/方法可以被设置为private/static/final,无用的参数可能被移除,一些方法可能被内联。
可通过设置-dontoptimize不进行方法内联和裁剪 - obfuscate:针对不是入口点的类和类成员进行重命名,保持入口点类原始名称,以保证入口点类通过原名可访问到。
- preverify:针对.class文件预校验,在.class文件中加入StackMa/StackMapTable信息,这样Hotspot VM在类加载时执行类校验阶段会省去一些步骤,因此类加载更快。
android虚拟机有自己一套代码校验逻辑(dvmVerifyClass),所以一般不校验,否则会拖慢打包速度,可通过-dontpreverify设置不校验。
4.6 Java中的泛型
泛型完全在编译器中实现,由编译器执行类型检查和类型推断,再生成普通的非泛型字节码,也称为擦除技术。编译器使用泛型类型信息保证类型安全,再在生成字节码前将其清除。
4.7lambda表达式编译规则:
lambda为java添加缺失的函数式编程特点。
函数式接口表示具有唯一的一个抽象方法的接口,如Runnable, Comparator
lambda表达式跟匿名内部类比:
关键字this:匿名内部类this指向匿名类,而lambda表达式的this指向包围lambda表达式的类;
编译方式:Java编译器将lambda表达式编译成类的私有方法,使用了Java7的invokedynamic字节码指令来动态绑定这个方法。匿名内部类则被编译成外部类&number的新类;
invokedynamic:支持动态语言的指令,允许方法调用可以在运行时指定类和方法,不必在编译的时候确定。字节码中每条invokedynamic指令出现的位置称为一个动态调用点,invokedynamic指令后面会跟一个指向常量池的调用点限定符,这个符会被解析为一个动态调用点。
为了使用java8中的lambda表达式特性,需要使用新的Jack工具链,新的Jack工具链直接将.java->.jack->.dex,而旧版javac工具链是.java->.class->.dex
from test.
Related Issues (2)
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from test.