前言
在Android应用开发中,热修复技术被越来越多的开发者所使用,也出现了很多热修复框架,比如:AndFix、Tinker、Dexposed和Nuwa等等。如果只是会这些热修复框架的使用那意义并不大,我们还需要了解它们的原理,这样不管热修复框架如何变化,只要基本原理不变,我们就可以很快的掌握它们。这一个系列不会对某些热修复框架源码进行解析,而是讲解热修复框架的通用原理。
1.热修复的产生概述
在开发中我们会遇到如下的情况: 1.刚发布的版本出现了严重的bug,这就需要去解决bug、测试并打渠道包在各个应用市场上重新发布,这会耗费大量的人力物力,代价会比较大。 2.已经改正了此前发布版本的bug,如果下一个版本是一个大版本,那么两个版本的间隔时间会很长,这样要等到下个大版本发布再修复bug,这样此前版本的bug会长期的影响用户。
3.版本升级率不高,并且需要很长时间来完成版本覆盖,此前版本的bug就会一直影响不升级版本的用户。
4.有一个小而重要的功能,需要短时间内完成版本覆盖,比如节日活动。
为了解决上面的问题,热修复框架就产生了。对于Bug的处理,开发人员不要过于依赖热修复框架,在开发的过程中还是要按照标准的流程做好自测、配合测试人员完成测试流程(因为没有一种框架是完美的,它们也只是适应大部分情况与使用的情况,它们也是有局限性的)。
2.热修复框架的对比
热修复框架的种类繁多,按照公司团队划分主要有以下几种:
类别成员:
阿里系 AndFix、Dexposed、阿里百川、Sophix
腾讯系 微信的Tinker、QQ空间的超级补丁、手机QQ的QFix
知名公司 美团的Robust、饿了么的Amigo、美丽说蘑菇街的Aceso
其他 RocooFix、Nuwa、AnoleFix
虽然热修复框架很多,但热修复框架的核心技术主要有三类,分别是代码修复、资源修复和动态链接库修复,其中每个核心技术又有很多不同的技术方案,每个技术方案又有不同的实现,另外这些热修复框架仍在不断的更新迭代中,可见热修复框架的技术实现是繁多可变的。作为开发需需要了解这些技术方案的基本原理,这样就可以以不变应万变。
部分热修复框架的对比如下表所示。
特性 AndFix Tinker/Amigo QQ空间 Robust/Aceso Sophix
即时生效 是 否 否 是 视情况而定
方法替换 是 是 是 是 是
类替换 否 是 是 否 是
类结构修改 否 是 否 否 是
资源替换 否 是 是 否 是
so替换 否 是 否 否 是
支持gradle 否 是 否 否 是
支持ART 是 是 是 是 是
支持Android7.0 是 是 是 是 是
我们可以根据上表和具体业务来选择合适的热修复框架,当然上表的信息很难做到完全准确,因为部分的热修复框架还在不断更新迭代。
从表中也可以发现Tinker和Amigo拥有的特性最多,是不是就选它们呢?也不尽然,拥有的特性多也意味着框架的代码量庞大,我们需要根据业务来选择最合适的,假设我们只是要用到方法替换,那么使用Tinker和Amigo显然是大材小用了。另外如果项目需要即时生效,那么使用Tinker和Amigo是无法满足需求的。对于即时生效,AndFix、Robust和Aceso都满足这一点,这是因为AndFix的代码修复采用了底层替换方案,而Robust和Aceso的代码修复借鉴了Instant Run原理,现在我们就来学习代码修复。
3.热修复框原理
目前代码修复主要有三种原理:底层替换方案,类加载方案,Instant Run方案。
方案 框架
底层替换 AndFix、Dexposed、阿里百川、Sophix
类加载 微信的Tinker、QQZone、QFix、Amigo、Nuwa
Instant Run Robust、Aceso
我们可以从上表发现,各个框架主要是使用什么原理实现。
底层替换原理:
底层替换就是直接通过Native层类实现即时的更新,但是由于是在Native层类原有上更改,所以有很多修改的限制,比如不能增减方法、字段,只能修改方法内的结果内容,过程如下图。
底层替换方案和反射的原理有些关联,ArtMethod结构体就是反射invoke()方法Native中的调用,AndFix采用的是替换ArtMethod结构体中的字段,这样会有兼容问题,因为厂商可能会修改ArtMethod结构体,导致方法替换失败。Sophix采用的是替换整个ArtMethod结构体,这样不会存在兼容问题。。
类加载原理:
类加载就是通过Dex分包方案,通过双亲委托模式进行优先加载方式实现类的替换。
类加载方案需要重启App后让ClassLoader重新加载新的类,为什么需要重启呢?这是因为类是无法被卸载的,因此要想重新加载新的类就需要重启App,因此采用类加载方案的热修复框架是不能即时生效的。
Instant Run原理:
除了资源修复,代码修复同样也可以借鉴Instant Run的原理, 可以说Instant Run的出现推动了热修复框架的发展。
Instant Run在第一次构建apk时,使用ASM在每一个方法中注入了类似如下的代码:
IncrementalChange localIncrementalChange = $change;//1if(localIncrementalChange !=null) {//2 localIncrementalChange.access$dispatch("onCreate.(Landroid/os/Bundle;)V",newObject[] {this, paramBundle });return; }
其中注释1处是一个成员变量localIncrementalChange ,它的值为$change,$change实现了IncrementalChange这个抽象接口。当我们点击InstantRun时,如果方法没有变化则$change为null,就调用return,不做任何处理。如果方法有变化,就生成替换类,这里我们假设MainActivity的onCreate方法做了修改,就会生成替换类MainActivity$override,这个类实现了IncrementalChange接口,同时也会生成一个AppPatchesLoaderImpl类,这个类的getPatchedClasses方法会返回被修改的类的列表(里面包含了MainActivity),根据列表会将MainActivity的$change设置为MainActivity$override,因此满足了注释2的条件,会执行MainActivity$override的access$dispatch方法,access$dispatch方法中会根据参数”onCreate.(Landroid/os/Bundle;)V”执行MainActivity$override的onCreate方法,从而实现了onCreate方法的修改。
借鉴Instant Run的原理的热修复框架有Robust和Aceso。
总结:
到这里,我们大概理解了三大热修复大概的原理,下一章我会解读各个框架中的局限性,上文也提到没有一个框架是完美的,所以我们要更好的去理解他们处在怎么样的局限性,以便我们在使用过程中避免不必要的的时间成本。