摘抄自<<Android 进阶解密>>一书
热修复框架核心技术主要有三类:分别是代码修复、资源修复与动态链接库修复
很多热修复框架都参考了Instant Run资源修复原理。
Instant Run部署有三种方式,它会根据代码的情况来决定采用哪种部署方式:
activity。修改一个现有方式中的代码采用hot swapactivity需要重启。修改或者删除一个现有资源文时会采用warm swapcold swap的情况很多,比如:添加或者删除修改一个字段与方法,添加一个类等AssetManager,通过反射调用addAssetPath方法加载外部资源,这样新创建的AssetManager就含有外部资源AssetManager类型的mAssets字段的引用全部替换为新创建的AssetManager主要有三个方案:分别是底层方法替换、类加载方案与Instant Run方案
Dex分包方案DVM的ByteCode限制,DVM指令集的方法调用指令invoke-kind索引为16bits,最多能引用65536个方法**LinerAlloc限制:在安装应用时,可能会提示INSTALL_FAILED_DEXOPT,产生原因就是LinerAlloc限制,DVM中的LinerAlloc是一个固定的缓存区,当方法数超出了缓存区大小时会报错。
为了解决65536与LinerAlloc限制,产生dex分包方案。该方案主要做是在打包时将应用代码分成多个Dex,将应用启动时必须用到类与这些类的直接引用类放到主Dex中,其他代码放到次Dex中。当应用启动时先加载主Dex,等应用启动完在动态加载次Dex,从而缓解主dex的65536与LinerAlloc限制。Dex方案主要有两种,分别是Google官方方案、Dex自动拆包与动态加载方案。类加载方案需要重启App后让ClassLoader重新加载新的类,为什么需要重启,因为类是无法卸载的,要想重新加载类就需要重启App,因此采用类加载方案的热修复框架无法及时生效。
虽然很多热修复框架采用了类加载方案,但是具体实现还是有区别的:
Element数组的第一个元素优先加载。diff,得到path.dex,再将path.dex与手机中的apk的classes.dex做合并,生成新的classes.dex,然后在运行时通过反射将classes.dex放在Element数组第一个元素。dex对应的Element取出来,之后组成新的Element数组,在运行时候通过反射用新的Element数组替换现有的Element数组。与类加载方案不同,底层替换方案不会再次加载新类,而是直接在Native层修改原有类,由于在原有类进行修改限制会比较多,且不能增减原有类的方法和字段,如果我们增加了方法数,那么方法索引也会增加,这样访问方法时会无法通过索引找到正确的方法,同样的字段也是,方法反射我们可以调用java.lang.Class.getDeclaredMethod。
在ART虚拟机中对应一个ArtMethod指针,ArtMethod结构体中包含了Java方法所有信息,包括执行入口、访问权限、所属类与代码执行地址等
替换ArtMethod结构体中的字段或者替换正给ArtMethod结构体,这就是底层替换方案。AndFix采用替换ArtMethod结构体中的字段,这样会有兼容问题,因为厂商可能会修改ArtMethod结构体,导致方法替换失败,Sophix采用替换整个ArtMethod结构体,这样就不存在兼容问题。底层替换直接替换了方法,可以立即生效不需要重启。采用底层替换方案主要以阿里系为主。
在第一次构建APK时,使用ASM在每一个方法中注入类似如下的代码:
IncrementalChange loaclIncrementalChange = $change;
if(loaclIncrementalChange !=null) {
loaclIncrementalChange .access$dispatch("onCreate.(Landroid/os/Bundle;)V",new Object[]{this,paramBundle});
return;
}当我们点击Instant Run,如果方法没有变换,则$change为null,就调用return不做处理,如果方法有变化,就生成替换类。假设Mainactivity的onCreate方法修改,就会生成 Mainactivity$overrid,实现了IncrementalChange 接口,同时生成一个AppPatchedLoaderImpl类,这个类的getPatchClasses方法会返回被修改类的列表,根据列表会将Mainactivity的$change设置为 Mainactivity$overrid,方法变化会执行Mainactivity$overrid的access$dispatch方法,在该方法中根据参数"onCreate.(Landroid/os/Bundle;)V执行Mainactivity$overrid的oncreate方法,从而实现方法修改。
借鉴该方案的热修复框架有美团的Robust与美丽说蘑菇街的Aceso。
主要指so库。热修复框架主要是更新so,基础原理就是加载so.
加载so主要用到System类的load和loadLibrary方法
System类的load方法传入参数是so在磁盘的完整路径,用于加载指定路径的so。System类的loadLibrary方法传入so的名称,用于加载App安装后自动从apk包中复制到/data/data/packagename/lib下的so.
so修复一种方案,就是将so补丁插入到NativeLibraryElement数组的前部,让so补丁的路径先返回,并调用Runtime的doLoad方法中会调用native的nativeload。
在Runtime nativeload函数中调用JVM_NativeLoad函数。JavaVMExt类型指针代表一个虚拟机实例,紧接着调用JavaVMExt的LoadNativeLibrary函数加载so.
LoadNativeLibrary函数总结:
so是否加载过,两次ClassLoader是否是同一个,避免so重复加载SharedLibrary,如果传入path对应的library为空指针,就将新创建SharedLibrary赋值给library,并将library存储到libraries中JNI_OnLoad函数指针,根据不同情况设置was_successful值,最终返回was_successful。so修复主要有两种方案:
NativeLibraryElement数组的前部,让so补丁的路径先返回和加载;System的load方法来接管so的加载入口;so文件object的缩写,见名思义就是共享的对象,机器可以直接运行的二进制代码。大到操作系统,小到一个专用软件,都离不开so。
so主要存在于Unix和Linux系统中。so库的名称和文件名so库的名称可任意,如daking。so库的文件名必须以lib开头。如libdaking.so,其中lib是必要前缀,daking才是这个库的名称。Android中的soso是与平台相关的二进制机器码,与ABI(Application Binary Interface)相对应,一个ABI表示相应的CPU的指令集与内存页管理,也对应于不同的C运行环境,所以so是有不同的系统版本的。
随着Android系统的快速发展,搭载Android的硬件平台也早已多样化了(对比WinTel联盟,直到2012年才新发展了Windows RT来适配ARM平台,2015年的Win10才进入 Raspberry Pi 2这类基于ARM的新型设备中),
现在已经运行在7个ABI:armeabi,armeabi-v7a (armeabi-v7a-hard),arm64-v8a,x86,x86_64,mips 和 mips64。为什么使用上面主要从软件开发的角度说明了为什么设计so以及开发者为什么使用so,由于Android基于Linux Kernl的,也继承了Linux中所有so相关的设计。
除了系统方面的原因,Android开发者还要知道以下几点:so机制让开发者最大化利用已有的C和C++代码,达到重用的效果,利用软件世界积累了几十年的优秀代码so是二进制,没有解释编译的开消,用so实现的功能比纯java实现的功能要快so内存分配不受Dalivik/ART的单个应用限制,减少OOM
应用程序定义的二进制文件尤其指so文件,如何运行在相应的系统平台,从使用的指令集,内存对齐到可用的系统函数库中,在Android 系统上,每一个CPU架构对应一个ABI:armeabi、armeabi-v7a、arm64-v8a、x86、x86_64、mips、mips64
不同的 Android 手机使用不同的 CPU,而不同的 CPU 支持不同的指令集。CPU 与指令集的每种组合都有专属的应用二进制接口,即 ABI。ABI 可以非常精确地定义应用的机器代码在运行时如何与系统交互。您必须为应用要使用的每个 CPU 架构指定 ABI。典型的 ABI 包含以下信息:机器代码应使用的 CPU 指令集。运行时内存存储和加载的字节顺序。可执行二进制文件(例如程序和共享库)的格式,以及它们支持的内容类型。在代码与系统之间传递数据的各种规范。这些规范包括对齐限制,以及系统调用函数时如何使用堆栈和寄存器。运行时可用于机器代码的函数符号列表 - 通常来自非常具体的库集。
什么是ABI:
Application Binary Interface的缩写。library或操作系统。.so)如何运行在相应的系统平台上等细节。ABI:armeabi、armeabi-v7a、arm64-v8a、x86、x86_64、mips、mips64。