从零开始初步实现安卓热修复功能

热修复,即热加载。是指在一个App正常打开之后,从外部加载本不属于它的一些资源并加以运用的功能。以下demo只是基于热修复的原理做到了初步实现,另一个关键点 dex插桩 还有待下一篇文章详细讲解,因此这一版的demo并不能投入实际的商业化运用

热修复的实现的途径有很多种,其中一种是通过反射来拿到当前应用程序的 ClassLoader 中的成员变量:pathList 所指向的实例。然后再通过反射这个实例,拿到其内部成员变量:dexElements数组 的值。由于安卓系统在加载一个类之前会从前往后地去遍历这个 dexElements数组,寻找当前需要加载的 .class 文件。因此,只要我们将外部下载的 apk包 或者是 .dex 文件路径生成的对象插入到 dexElements数组 的最前方,就可以达到顶替数组后面原有的同名 .class 文件的目的,从而实现外部热修复。示意图如下(patch.dex就是外部加载进来的修复包):


因此,在整个热加载功能实现过程中(包括插件化和so库动态加载):Classloader -> 内部DexPathList类型的成员变量 -> DexPathList中的成员变量Elements数组的更改,是整个功能中比较核心的一环,运用类加载的实现方式完成热修复的框架,源码中一定有反射并修改elements数组的相关逻辑。

根据以上提到的思路,我们来看一下核心的相关代码:

拿到 ClassLoader 和 修复包的存储路径 之后,就可以开始准备做 java反射了,这一块的逻辑我封装到了 installSecondaryDexes 方法中

根据不同的系统SDK版本执行不同的反射逻辑(我们以 api19 及其以上的代码为例)

接下来获取到 dexElements 数组,准备在数组第一位插入外部载入的修复包路径

由上图可见,截图中的最后一个方法 makeDexElements 就是插入外部修复包路径的封装方法,这个方法的第三个实参又是一个方法,这个方法是根据不同的手机SDK版本,将修复包的路径封装成不同类型的对象组成的数组,这个数组最后会被插入到 dexElements 数组当中。经过了 expandFieldArray 这个方法的执行之后,原先存储 外部修复包 的路径下就多出了一个 .dex 文件,或者 .odex 和 .vdex 文件。makeDexElements 方法的代码如下:

我们再回到外层的 expandFieldArray 方法,其内部的逻辑是这样的:首先通过反射拿到dexElements的取值,然后将上图方法获取到的 object[] 插入到数组的最前面。这个被插入的 object[] 数组就是外部修复包存储路径集合编译后形成的队列,也就是外部修复包的资源和 .class 队列

以上步骤完成之后,当前App的 dexElements 的状态就变成了这样,理论上是可以顶替原包的同名 .class 文件了:

接下来我们做一些外部的封装,比如说断点续传下载外部修复包,以及在Splash页面上做热修复准备处理等等,original包和fix包的代码都在下面的git链接上了:

https://github.com/liuchenguangqnm/hot_fix_example

然后我们再在俩包的MainActivity上分别写上不同的吐司显示准备测试:

然后找到 original_package 的 Appconfig 类,配置好修复包的下载地址,以及下载到本地的存储文件名

最后就可以安装看效果了:

首先第一次打开,因为修复包需要下载时间,所以第一次打开App或者断网的时候修复包是没有插入完成的,所以此时点击按钮提示如下:

等到第一次的修复包插入完成之后,关闭MainActivity,再次打开,点击测试按钮,吐司显示如下:

以上代码经本人测试,可以顺利在安卓各个模拟器和小米4上面跑通。由于开篇的时候我就说过了,此版本demo由于没有做 dex插桩,因此不能投入商业化使用,所以,以上的demo在华为手机上是跑不通的。

那么什么是dex插桩呢?

我们首先围绕上面讲到的问题出发:为什么有的手机按照demo上的代码运行可以正常走通,有些就走不通,难道有的手机加载类的时候不是通过遍历 dexElements 数组获得 .class 文件的吗?当然这是不可能的。以上demo之所以走不通,是因为新版本的手机SDK为了提高App的启动速度,已经在我们安装应用的时候预先加载了安装包里的所有 .class 文件的索引,有了这个索引,我们再打开App的时候,系统就再不用去 遍历DexElements数组 寻找对应的 .class 文件了。

因为每次加载这个类,新安卓版本的系统都不用再去遍历 DexElements数组 了,因此我们在 App启动之后,再去对 DexElements数组 的内容进行操作,其实都是无意义的,因为系统根本不会再次遍历它了。而解决这个问题的途径之一就是 dex插桩!

首先,我们要清楚的是,系统在安装了一个新的App之后,首先会查看这个 App 里面有多少 .dex 文件。如果一个 .class 文件里面使用到的所有类,都在一个 .dex 文件之中存放的话,那么系统就会给这个类打上一个 CLASS_ISPREVERIFIED 标记,有了这个标记,下次App如果要再加载这个类的时候,就不会再次遍历 DexElements 数组了;如果一个 .class 文件里面使用到的所有类,分别在两个 .dex 文件之中的话,那么为了避免在类运行过程中,因为其中一个 .dex 文件的缺失而导致异常,此时这个类不会被打上 CLASS_ISPREVERIFIED 标记,每次我们要加载它的时候,系统还是要遍历一次 dexElements 数组,确保所有的 .dex 文件都是全乎的

因此,我们需要实现的就是,在安卓打包的时候,给所有有可能出bug的类的构造方法里面都插入一行初始化其它 .dex 文件中的类型的代码,只要有了这行代码,这个类在每次加载之前就必须要重新过一遍 dexElements 数组,我们的修复包就可以趁着这个时机,顶掉存在bug的类。这种在打包时修改 .class 文件内容的技术,就叫做 dex插桩

由于时间和精力有限,再加上dex插桩技术本人也需要花更多的时间去测试和研究,因此我会在下一篇博客继续完善上面分享过的demo

以上,本篇完结

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容