美文网首页Android热修复技术
NuWa热修复原理基础详细解说——初识,热修复

NuWa热修复原理基础详细解说——初识,热修复

作者: 这个美嘉不姓陈 | 来源:发表于2017-02-21 10:34 被阅读378次

    前言:不得不跟正在看这篇文章的宝宝们说一句,作为菜鸟的我,看了很多大神的文章,对于热修复也是初识,有什么写的不对的地方,还望大家指导,此篇文章也作为自己的笔记,便于自己总结。

    做这个笔记之前我看了一下几篇文章,记下了好的解说图,和好的观点,附上链接,表示感谢:

    热修复,没你想的那么难

    热补丁动态修复框架小结

    安卓热补丁动态修复技术介绍 

    一、什么是热修复:

    相信很多人第一次听说热修复的时候也是一脸茫然,那么我就用自己的话来跟大家讲解一下。

    当一个已经上线的APP突然发现一个严重的bug的时候,除了重新发版,让用户重新下载覆盖安装以外,还有没有别的方法来解决呢?如果可以有一个动态修复的方法就好了,那就是热补丁动态修复技术,也就是我们说的热修复。向用户下发Patch,在用户无感知的情况下,修复bug问题。这里用到的是android dex分包方案,后面会具体解释如何实现的。

    这里我们先说一下Android的插件化,Android插件化分为三大块:热部署动态加载资源四大组件动态加载

    分别举例:

    热部署 ------------------------------热修复

    动态加载资源-------------------动态换肤

    四大组件动态加载----------模块化开发

    二、基于的原理——心急吃不了热豆腐

    首先,就要从Android的ClassLoader体系来说起:

    ClassLoader顾名思义,就是在Java中加载一个类需要用到的。

    在Android中有三大ClassLoader,分别为URLClassLoader,PathClassLoader,DexClassLoader.其中:URLClassLoader:只能用于加载jar文件,但是由于 dalvik 不能直接识别jar,所以在 Android 中无法使用这个加载器。

    这里我们重点来说后面这两个加载类:

    PathClassLoader:前面说过热修复是用到了dex分包方案(后面会说什么是dex文件),那么这个PathClassLoader就是用来去找dex文件的类。当apk安装后,PathClassLoader会去读取/data/dalvik-cache/dex文件,例如我们安装了一个包名为com.meijia.xxx的apk,那么安装后就会在/data/dalvik-cache目录下,生成一个data@app@com.meijia.xxx-1.apk@classes.dex的ODEX文件(后面会讲解什么是ODEX文件),而PathClassLoader找的就是这个ODEX文件,如果说找不到这个文件,就证明apk还没有安装,报错就是ClassNotFoundException。

    DexClassLoader:顾名思义,DexClassLoader就是用来加载dex的加载类,就是从.jar和.apk类型的文件内部加载classes.dex文件。可以用来执行非安装的程序代码,也就指的是插件中的apk代码。

    它的构造函数包含四个参数:

    1.dexPath:就是目标类所在的APK或者jar文件的全路径。

    2.dexOutputDir:就是dex在APK或者jar文件中解压后的dex文件存放路径

    3.libPath:指目标类中所使用的C/C++库存放的路径

    4.classLoad:是指该装载器的父装载器,一般为当前执行类的装载器

    到这里,我们就知道了1.PathClassLoader在这里是作为类加载器,2.DexClassLoader是用来加载classes.dex文件的。继续。

    PathClassLoader和DexClassLoader都是继承BaseDexClassLoader这个类,看源码如图:

    这里我们通过注释可以看到1.其中DexPathList pathList就是多dex的结构列表。2.其中pathList里面包含一个Element [ ] dexElements数组就是dex列表,每一个element就是一个dex文件。

    那好,当PathClassLoader加载类加载的时候会遍历Element [ ] dexElements,如果找到对应类则加载,找不到,就会继续从下一个dex文件中查找,那么我们就明白了,试想,如果我们将补丁插件dex文件插入到Element [ ] dexElements的最前面,理论上不同dex中有相同的类名存在时,会优先加载第一个类,找到了就返回,不会再继续查找下一个dex文件了,这就是我们说的基于dex分包的热修复的原理。

    到这里,我们就知道了热修复就是在ClassLoader(类加载器)中插入一个dex文件。

    那么什么是dex文件?什么又是ODEX文件呢?它们和class是什么关系?为什么Android只能识别dex文件,不能直接识别class文件呢?

    先看一下class的结构,如图(摘自邓凡平老师博客)

    我们可以看到一个class文件中包含很多版本信息,常量等信息。

    dex文件就是将整个Android项目中的所有class文件合并成一个或者几个dex文件,当两个或几个class文件中有重复的字符串,那么dex就只存一份就可以了,换种说法就是dex是对class的压缩。

    所以,在Android dalvik虚拟机中是无法识别一个class文件的,因为他们的结构不同,一个dex文件的结构如图

    然后什么是ODEX呢,Android dalvik虚拟机也不是直接识别dex文件的,当APK被安装的时候,虚拟机会实现一次优化,就是将dex文件转换成odex文件,这次转换是为了对不同的手机硬件做对应的优化,而class转换成dex,是针对不同平台的优化,两者意义上是不同的。

    然而,就在这优化过程中,在虚拟机启动的时候会有很多参数,其中有一项叫做verify的选项,当这个选项被开启之后,就会实行一个类的校验,具体校验内容就是,如果static方法、private方法、构造函数等,其中的直接引用(第一层关系)到的类都在同一个dex文件中,那么该类就会被打上CLASS_ISPREVERIFIED标志

    校验过程如下:

    一旦这个类CLASS_ISPREVERIFIED标志被打上,那么我们也就不能从别的dex文件中替换这个类了,那么我们之前说的在Element [ ]中插入dex的方法就无用了,知道了原因,办法就好想了。

    不就是如果这个dex中的类没有引用其他dex文件中的类,就会被打上CLASS_ISPREVERIFIED
    标志么?那么我们就让他引用就好了。

    三、如何阻止CLASS_ISPREVERIFIED标记

    到这里,试想,如果我们在所有的类的构造函数中,引用一个其他dex文件中的类的方法的话,是不是就可以防止被打上CLASS_ISPREVERIFIED标记了。

    其中,hack.dex文件是要在最先加载进来的,不然如果当应用启动的时候,hack.dex没有先加载进来的话,这些引用它的类,就会报错,找不到hack.dex中的AntilazyLoad这个类。

    那么问题来了?

    如何去修改一个类?又如何在应用启动时最先加载hack.dex这个文件呢?

    不同的修复方案的解决办法是不同的,这里就不一一介绍了,因为如果要说的话,要说的东西太多,我觉得自己还说不好,就搜罗网络上热门的方案大致以下几种开源框架。

    https://github.com/dodola/HotFix 

    https://github.com/jasonross/Nuwa 

    https://github.com/bunnyblue/DroidFix

    其中鸿洋大神的文章是用HotFix解说的:HotFix解说

    涛哥的文章是用Nuwa解说的:Nuwa解说

    总结:在学习一项新知识的时候,需要很大的耐心,对于基础不是很好的同学,会在学习的过程中发现越来越多自己不懂的地方,这时候请不要烦躁,请静下心来,反复对敲,不懂的地方更是要耐心的去找资料去学习,当你明白了一项新知识以后,你会发现,你学到的比你想象的还要多。以此共勉,加油~

    相关文章

      网友评论

        本文标题:NuWa热修复原理基础详细解说——初识,热修复

        本文链接:https://www.haomeiwen.com/subject/skeewttx.html