Andfix热修复原理

1、前言

最近腾讯弄出一个Tinker热修复框架,那么本文先不介绍这个框架,先来介绍一下阿里的一个热修复框架AndFix,这个框架出来已经很长时间了,可是看网上没有太多很是详细的讲解,这里就来作一次分析。正好项目中要使用到。首先这个框架是开源的:https://github.com/alibaba/AndFix 其实在最先的时候我已经分析了阿里的另一个热修复框架:Dexposed框架,还不了解的同窗能够点击这里查看:Dexposed框架原理解析以及使用 当时介绍这个框架的时候发现他的实现原理很简单:java

Android中热修复框架AndFix原理解析及案例使用

他的思想彻底来源于Xposed框架,完美诠释了AOP编程,这里用到最核心的知识点就是在native层获取到指定方法的结构体,而后改变他的nativeFunc字段值,而这个值就是能够指定这个方法对应的native函数指针,因此先从Java层跳到native层,改变指定方法的nativeFunc值,而后在改变以后的函数中调用Java层的回调便可。实现了方法的拦截功能。git

2、源码分析github

那么本文介绍的AndFix框架相对于Dexposed框架来讲又有什么区别呢?其实区别就在于AndFix框架更加轻便好用,在进行热修复的过程当中更加方便了。固然这个优势在后面的Tinker框架也是能提现出来的。这个框架的原理是:直接在native层进行方法的结构体信息对换,从而实现完美的方法新旧替换,从而实现热修复功能。下面经过分析他的源码来看他的具体实现,下载完源码以后导入工程:编程

Android中热修复框架AndFix原理解析及案例使用

这里能够看到,由于在native层须要替换新旧方法结构体信息,因此这里确定要作的工做就是虚拟机的兼容问题,这里作了art和dalvik的分开处理逻辑,下面来看一下这个框架的基本使用规则:安全

Android中热修复框架AndFix原理解析及案例使用

用法仍是很简单的,这里的修复包是直接放在本地的,在实际操做中会从网上去下载。下面就开始分析源码,这里有一个主要的类就是PatchManager:服务器

Android中热修复框架AndFix原理解析及案例使用

第1、PatchManager类初始化网络

在这个类的构造方法中作了两件事,一件事是初始化AndFixManager类,一件事是建立修复包存放的沙盒目录。这里先来看第二件事建立沙盒目录:app

Android中热修复框架AndFix原理解析及案例使用

能够看到这个目录是:/data/data/xxx/files/apatch/xxx.apatch框架

Android中热修复框架AndFix原理解析及案例使用

当咱们从网上下载好修复包apatch文件以后,会调用addPatch方法,这时候会把修复包复制到这个地方,之后再次启动时就会遍历这个目录加载apatch文件。eclipse

第2、AndFixManager的初始化

下面继续来看AndFixManager初始化操做:

Android中热修复框架AndFix原理解析及案例使用

在这个初始化中也是干了两件事:一件事是判断当前环境是否支持热修复,一件事是初始化修复包安全校验的工做,先来看一下判断是否支持操做:

Android中热修复框架AndFix原理解析及案例使用

这里支持的条件是:非YunOS系统,Android2.3-7.0系统版本,热修复native层设置是否成功。这里咱们看到应该值得关心的是setup操做,这个操做实际上是native层进行的,能够直接看一下具体代码:

Android中热修复框架AndFix原理解析及案例使用

这里主要作了一些初始化操做,获取一些函数指针,准备后续的replaceMethod函数中使用:

一、在libdvm.so动态获取dvmDecodeIndirectRef函数指针和获取dvmThreadSelf函数指针。

二、调用dest的 Method.getDeclaringClass方法获取method的类对象clazz。

继续回到AndFixManager的初始化中的第二件事:修复包安全校验工做,其实这里只是作了初始化操做,而真正校验的工做在后面,主要是经过比对应用的签名和修复包的签名信息。后面再看吧!

第三:PatchManager的初始化操做

Android中热修复框架AndFix原理解析及案例使用

这里的初始化作了一件事:是判断当前PatchManager的版本号是否发生变化,若是发生变化就清空本地全部的修复包。若是没有变化,直接调用初始化修复包方法

Android中热修复框架AndFix原理解析及案例使用

这个方法其实就是咱们上面提到的逻辑,会遍历沙盒中修复包目录中全部的修复包文件,而后把它添加到修复列表中。

第四:PatchManager的添加修复包操做

这里提供了两个添加修复包的方法,一个是接受文件样式的参数:

Android中热修复框架AndFix原理解析及案例使用

这个方法就是上面的那个initPatchs方法中调用的地方,这里会把目录下全部的修复包文件加到列表中。这里来看一下另一个主要类Patch的初始化操做:

Android中热修复框架AndFix原理解析及案例使用

这个初始化的主要工做就是经过JarFile类解析修复包文件,读取他的META-INF\PATCH.MF文件内容,获取须要修复类的名称,多个修复类之间用逗号分隔,相似于这样的样式:

Android中热修复框架AndFix原理解析及案例使用

这里的Utils类就是咱们须要修复的方法所属的类名,可是这里他又作了处理就是在每一个类的后面加了后缀:_CF。这里分析完了全部须要修复的类名以后保存到一个列表中,后面会经过修复包名称获取到他的修复类名称列表。

还有一个添加修复包文件的方法,接受的是文件路径参数:

Android中热修复框架AndFix原理解析及案例使用

这个方法接受的是修复包文件路径,首先会把这个文件拷贝到上面提到的沙盒目录中以便下次进行遍历操做,拷贝以后继续调用上面的addPatch方法添加到列表中,最后就在调用加载修复包操做了。

注意:

第一个方法接受文件样式的方法实际上是须要结合上面的initPatchs方法一块儿使用,他调用的场景是:本地沙盒目录中已经有了修复包文件,而且版本号没有发生变化,这样每次启动程序的时候就会调用初始化操做,在这里会遍历沙盒目录中全部的修复包文件,而后调用这个方法添加到全局文件列表中。

第二个方法接受的是文件路径样式,这个方法使用的场景是版本号发生变化,或者是本地沙盒中没有修复包文件。好比第一次操做的时候,会从网络上下载修复包文件,下载成功以后会把这个文件路径经过这个方法调用便可,执行完以后也会主动调用加载修复包的操做了,好比这里第一次在SD卡中放了一个修复包文件:

Android中热修复框架AndFix原理解析及案例使用

只要调用了这段代码以后就能够走完了全部的流程了:拷贝修复包到沙盒目录中,加载修复包文件。

第五:PatchManager的加载修复包操做

Android中热修复框架AndFix原理解析及案例使用

这个方法有两个地方会调用到:一个是上面提到的那个接受修复包路径的addPatch方法,一个是调用完接受修复文件类型的addPatch方法以后手动调用一次,相似于这样:

Android中热修复框架AndFix原理解析及案例使用

这个方法内部主要是经过Patch类获取修复包全部的修复类名称,以前已经介绍了Patch类的初始化操做,在哪里会解析修复包的MF文件信息,获取到修复包须要修复的类名而后保存到列表中,这里就经过getClasses方法来获取指定修复包名称对应的修复类名称列表,而后在调用AndFixManager的fix方法便可,下面再来看一下AndFixManager的fix方法的实现逻辑:

Android中热修复框架AndFix原理解析及案例使用

这个方法有点长,并且内容也比较多,这里主要作了这么几件事:

第一件事:使用上面初始化完成的校验类进行修复包的校验工做,这里的校验就是比对修复包的签名和应用的签名是否一致:

Android中热修复框架AndFix原理解析及案例使用

这个具体实现逻辑不用介绍了,你们能够下载源码本身分析。

第二件事:使用DexFile和自定义类加载器来加载修复包文件

这个其实和使用DexClassLoader加载原理相似,并且DexClassLoader内部的加载逻辑也是使用了DexFile来进行操做的,而这里为何要进行加载操做呢?由于咱们须要获取修复类中须要修复的方法名称,而这个方法名称是经过修复方法的注解来获取到的,因此咋们得先进行类的加载而后获取到他的方法信息,最后经过分析注解获取方法名,这里用的是反射机制来进行操做的。

Android中热修复框架AndFix原理解析及案例使用

这个加载完类以后就会继续调用fixClass方法,再来看一下fixClass方法实现:

Android中热修复框架AndFix原理解析及案例使用

这里主要是经过反射获取指定类名须要修复类中的全部方法类型,而后在获取到他的注解信息,上面已经分析了经过DexFile加载修复包文件,而后在加载上面Patch类中的getClasses方法获取到的修复类名称列表来进行类的加载,而后在用反射机制获取类中全部的方法对应的注解信息,经过注解信息获取指定修复的方法名称,看一下这个注解的定义:

Android中热修复框架AndFix原理解析及案例使用

这里提供了两个方法,一个是获取当前类名称,一个是获取当前方法名称,能够看一下具体事例:

Android中热修复框架AndFix原理解析及案例使用

上面解析完注解信息以后获取到了方法名称,紧接着就调用了replaceMethod方法开始了方法的替换操做

Android中热修复框架AndFix原理解析及案例使用

这里还会作一件事就是经过上面获得的修复新的方法信息以及须要修复的旧方法名称来操做,不过这里得先获取到旧方法类型,能够看到修复的新旧方法的签名必须一致,所谓签名就是方法的名称,参数个数,参数类型都必须一致,否则这里就报错的。进而也修复不了了。最后在调用了AndFix的addReplaceMethod方法进行native层的修复工做:

Android中热修复框架AndFix原理解析及案例使用

这里会作虚拟机的区分处理,可是他们大体的处理逻辑都是一致的,这里来看一下dalvik的处理机制:

Android中热修复框架AndFix原理解析及案例使用

这里的操做也是很是简单的,主要是经过上层传递过来的新旧方法类型对象,经过JNIEnv的FromReflectedMethod方法获取对应的方法结构体信息,而后将其信息进行替换便可,这里能够看到替换的信息也是很是多的,并且也看到了咱们以前介绍Dexposed框架用到的一个字段值nativeFunc,这个就是指定这个Java方法对应的native方法。可是在这以前也会看到有一段代码是用来获取修复方法的类信息的,这里主要是用来作修复方法的类初始化操做,在以前咱们看setup方法的时候知道,那里作了这么两件事:

一、在libdvm.so动态获取dvmDecodeIndirectRef函数指针和获取dvmThreadSelf函数指针。

二、调用dest的 Method.getDeclaringClass方法获取method的类对象clazz。

而后在这里就开始获取修复方法对应的类信息,经过调用方法的getDeclaringClass获取方法对应的类对象clazz,而后在调用dvm方法获取到对应的类结构体信息ClassObject,最后在设置他的状态信息标记这个类已经初始化完毕了。

注意:

这里能够看到经过一个类对象clazz类型能够获取到对应的结构体信息ClassObject,这个操做也是很是实用的,由于这个结构信息中有一个字段pDvmDex值,而这个值就是DvmDex结构体信息也就是底层对应的dex文件信息,因此说咱们能够经过一个类信息获得他对应的dex文件信息。

3、流程总结

到这里就讲解完了整个框架的全部技术点了,上面可能说的有点乱,下面在来大致总结一下,首先来看一张简单的流程图信息(图片有点大,能够下载看高清大图):

Android中热修复框架AndFix原理解析及案例使用

第1、Patch类负责解析每一个修复包apatch文件信息,获取全部须要修复的类名

这个类的初始化操做中会经过传递进来的修复包文件,使用JarFile类进行文件解析,读取他的META-INF\PATCH.MF文件信息,主要经过读取Patch-Classes字段值来获取须要修复的类名称,多个类名称之间用逗号分隔,并且每一个类名称都有一个后缀:_CF。解析完成以后就会保存到一个用修复包名称做为key的HashMap中,后面会经过修复包名称参数来调用getClasses方法获取对应修复的类名称列表。

第2、PatchManager负责管理多个Patch类也就是多个修复包信息

主要方法包括初始化,添加修复包,加载修复包,这个类是提供给外界调用的一个入口类,这里有两种方式调用:

一种方式是先调用init方法进行初始化,在这个初始化方法中会判断当前的版本号,若是版本号发生变化就会清空本地全部的修复包文件,若是没有变化就加在全部的本地修复包文件,而这个本地目录就是沙盒中存放修复包文件的目录:/data/data/xxx/apatch/xxx.apatch,而后在调用loadPatch方法进行修复包的加载工做。

一种方式是本地没有修复包文件,也就是第一次操做的时候可能须要从服务器下载修复包文件,这时候会把下载下来的修复包文件路径经过调用addPatch方法进行添加操做,而这里的添加操做包括了:先把修复文件拷贝到上面的沙盒修复包目录中,而后在调用loadPatch方法进行加载工做。

第3、AndFix类主要是和native层交互直接替换方法

这个类主要就是几个native方法用来和底层进行交互的操做,而这些方法都是会被AndFixManager进行调用的。

第4、AndFixManager类主要是负责管理AndFix类

主要方法包括加载每一个修复包中须要修复的类,解析出每一个类的注解信息获取该类须要修复的方法名称,初始化的时候会进行修复包的校验工做,主要经过对比修复包和应用的签名信息。因此能够知道每一个修复包是须要进行签名操做的,而后他的fix方法会使用DexFile类进行加载修复包文件,调用Patch的getClasses方法获取到全部须要修复的类名称进行加载操做。而后在调用fixClass方法,在这个方法中主要经过遍历修复类中全部指定MethodReplace注解信息的方法信息,而后在调用replaceMethod方法进行替换操做,而在这个方法中也会经过新方法的Method类型和注解信息中须要修复方法的名称来获得旧方法的Method类型,最终调用AndFix的native方法replaceMethod进行替换操做,因此这里能够看到替换的新旧方法的签名信息必须一致,否则无效,也就是方法的名称,参数个数,参数类型必须保持一致才能够。

第5、Native层方法

在native层中会作art和dalvik虚拟机的区分处理工做,他们大体的逻辑都是一致的:

dalvik 模式下的Java hook

一、在libdvm.so动态获取dvmDecodeIndirectRef函数指针和获取dvmThreadSelf函数指针。

二、调用dest的 Method.getDeclaringClass方法获取method的类对象clazz。

三、调用dvmDecodeIndirectRef方法,获取clazz的ClassObject*

四、通关 env->FromReflectedMethod方法获取dest的Method结构体函数的指针

五、替换method结构体的成员数据

art模式下的java hook

一、art模式中,咱们直接经过 env->FromReflectedMethod获取到ArtMethod函数指针。

二、而后直接替换ArtMethod结构体的成员数据指针

4、框架使用案例

上面介绍完了原理,下面若是不用案例来作分析,那都是白扯淡,这里咱们就用一个简单的案例来进行实际操做一下,并且在这个过程当中会发现一个神奇的工具apatch,这里的例子很简单,本地定义一个获取版本号的方法:

Android中热修复框架AndFix原理解析及案例使用

这时候咱们获得这个值,而后显示在界面上,而后开始出release包,这里直接用eclipse构造签名文件(这个签名文件要记得保存好,后面会使用到)出包了。等包上线发布以后,忽然发现这个版本号错了,应该是1.0.2,那么这时候就须要进行热修复了,操做很简单:

第一步:修改这个方法返回值为1.0.2

第二步:继续使用上面的签名文件进行签名获得了一个修复以后的apk包

第三步:使用神器apatch进行线上发布的release包和此次修复的fix包进行比对,获取到修复文件apatch

java -jar apkpatch.jar -f app-release-fix.apk -t app-release-online.apk -o C:\Users\jiangwei1-g\Desktop\apkpatch-1.0.3 -k jiangwei.keystore -p 123456 -a jiangwei -e 123456

这里在使用命令的时候须要用到签名文件,由于在前面分析代码的时候知道会作修复包的签名验证。这里获得了一个修复包文件以下:

Android中热修复框架AndFix原理解析及案例使用

并且会产生一个diff.dex文件和smali文件夹,这个就是修复类的文件对应的dex文件和smali代码:

Android中热修复框架AndFix原理解析及案例使用

Android中热修复框架AndFix原理解析及案例使用

而咱们用压缩软件能够打开apatch文件看看:

Android中热修复框架AndFix原理解析及案例使用

能够看到这里的classes.dex文件其实就是上面的diff.dex文件,只是这里更像是Android中的apk文件目录格式,一样有一个META-INF目录,这里存放了签名文件以及须要修复类信息的PATCH.MF文件:

Android中热修复框架AndFix原理解析及案例使用

签名文件就很少说了,来看一下PATCH_MF文件信息:

Android中热修复框架AndFix原理解析及案例使用

Patch_Classes字段包含了须要修复的类的名称信息了。

第四步:这里为了演示方便,直接把上面的修复文件拷贝到sd卡中,而后调用PatchManager的addPatch方法:

Android中热修复框架AndFix原理解析及案例使用

第五步:运行程序

Android中热修复框架AndFix原理解析及案例使用

这时候能够发现版本号已经修复成了1.0.2了。

5、apatch工具原理解析

上面看到案例使用比较简单,可是看到有一个比较牛逼的工具就是apatch,能够生成有方法变更的类所在的dex文件,那下面就来一块儿看看他的实现原理,没找到源码,直接使用jd-gui查看apatch.jar文件了:

Android中热修复框架AndFix原理解析及案例使用

这里的核心代码就是这部分,会把有方法变更的类信息列表对象DexBackedClassDef借助baksmali类写入到smali文件中,而后在借助DexBuilder和SmaliMod类把smali类变成dex文件,也就是最终的diff.dex文件了。那么下面在来看一下这个变更的DexBackedClassDef类列表信息如何获得的:

Android中热修复框架AndFix原理解析及案例使用

在这里使用DexBackedDexFile类进行加载新旧的dex文件,而后开始比对具体方法实现变更状况,主要是方法:compareMethod的实现:

Android中热修复框架AndFix原理解析及案例使用

这里会调用方法的getImplementation方法来判断新旧方法的实现发生变更了,若是有就把当前的类对象加入变更列表中便可。

因此从这里能够看到这里实际上是彻底借助了第三方的功能:能够把dex变成smali文件的baksmali工具包、能够把smali变成dex文件的smali工具包。而这两个工具包的源码以前在介绍apktool反编译工具的时候说到了,想查看源码的同窗能够查看这篇文章:反编译利器apktool的源码解析。从这里能够看到,咱们后续再处理dex,smali等文件格式的时候这两个工具包用的很是多。

6、框架技术总结

到此这次修复操做就完成了。咱们的讲解工做和案例演示工做也完成了,下面来总结一下这个框架的知识点,不过先来看一张大图(能够点击下载查看高清大图):

Android中热修复框架AndFix原理解析及案例使用

第1、核心技术点

从上面能够看到AndFix框架的技术点主要包括:

一、使用apatch工具生成修复包文件,主要借助baksmali和smali工具包实现

二、Java层传递新旧方法类型对象,到native层获取其对应的结构体信息实现完美替换新旧方法结构信息

第2、优势和局限性

优势:从上面能够看到这个框架的优势在于轻巧便捷,集成成本低,维护性强。

局限性:从上面的代码分析能够看到这个框架的局限性仍是不少的,特别是他只能修复对应已经存在的方法,好比如今我想增长一个方法确定不行的,若是想给修复方法增长参数信息也是不能够的,这个局限性就很是大了。还有一个局限性就是只能进行代码修复,资源是没法作到的。因此从这里能够看到这个框架更偏重于方法的热修复操做。

项目下载地址:http://download.csdn.net/detail/jiangwei0910410003/9678441

工具下载地址:http://download.csdn.net/detail/jiangwei0910410003/9678885

相关文章
相关标签/搜索