Android 热补丁技术详解大全

热修复详解与实战#

前言#

随着Google推出Android平台之后,AndroidAPP数量激增,带来的安全类技术需求增长,热修复就是安全类技术的典型产物。2016年起热修复、插件化、动态化技术如雨后春笋般爆发,三者技术上存在一定交集,但热修复更聚焦线上BUG的无感知修复。本文参考了一系列技术文章、书籍、开源框架、商业产品。主要讨论了热修复的以下几点内容:

  • 热修复的发展历史
  • 热修复的定义
  • 热修复的用途
  • 热修复的实现思路-分类介绍
  • 热修复的实现思路选型
  • 热修复的技术方案-分类介绍
  • 热修复的技术方案选型

本文适合以下目的的读者:

  • 了解热修复概念术语
  • 了解热修复的用途
  • 了解为新项目集成热修复功能的成本
  • 现有热修复技术方案的选型对比

热修复的定义#


图1-正常开发流程

正常发版流程是开发者打包4.0版本APK,运营人员将最新APK推送到各大手机应用市场、用户手机通知栏,引导用户下载安装最新版的APK。一般而言用户就能正常体验App的新功能了。一旦出现异常情况,用户在使用新版App的过程中,遇到了bug,如App闪退、App页面显示错误等情况,可能会给用户造成困惑。为了解决用户的困惑,开发者需要紧急修复该bug,并重新走发版流程,引导用户重新下载、安装4.1版本APK。

好消息是一个bug被开发者消灭了;坏消息是用户在漫长的等待过程中,卸载了App,公司最终因为这个bug损失了一批用户。这种负面情况,在按用户流量给企业估值的做法造成毁灭性的打击,这个经济成本是大多数公司无法承担的。

因此,即时、快速、无感的修复线上bug,成为了安全类技术的热门话题。

关于热修复的定义,笔者参考阿里发布的《深入探索Android热修复技术原理》[1]一文:

App直接从云端下拉补丁直接应用生效,重构了传统的App发版流程。


图2-集成热补丁技术后的发版流程

可以看到在遇到bug后,开发人员可以通过修复,并且打出补丁,通知App自动拉取的方式省去了提示用户去应用市场下载,点击通知栏从服务器下载最新APK等“烦人”的做法。让一个Bug对企业的负面影响降到最低。

作为企业技术决策人员,决策一个项目是否应该采取热修复技术,取决于企业是否需要以下几项热修复天然的优势:

  • 无需推送应用市场、无需等待应用市场审核
  • 无需提示用户下载最新APK
  • 实时无感修复
  • 用户感知小,线上响应快,企业损失最低

修复Bug、修复bug、修复bug,重要的话说三遍,热修复技术最重要的用途就是无感修复bug。

热修复的实现思路#

思路概览#

用户在操作一款App的过程中,能够体验到的内容如图所示,在不讨论Hybrid技术、Web开发技术的前提下,我们先承认一个简单的事实:安卓操作系统加载了APP安装包下的某些文件,换句话说APK的主要组成部分,支撑了App页面的显示和加载。用户在使用App过程中遇到了bug,是由于Apk中的某些内容遇到了预期之外的情况导致。作为开发者,若想修复这种预期之外的情况,就应该修复Apk中的主要内容来达到目的。


图3-某电商App首页

一个APK的组成部分如下

名称 用途
assets 静态资源目录,如zip资源、网页资源
com/google 特殊包名,存储一些三方依赖的文件如AIDL、cpp源码
lib 子目录armeabi存放的是一些so文件
META-INF 存放的是签名信息;编译生成一个apk包时,会对所有要打包的文件做一个校验计算,并把计算结果放在META-INF目录下
res 静态资源图片、动画、字符串、主题、颜色等
resources.arsc 编译后的二进制资源文件的索引(apk文件的资源表索引)
AndroidManifest.xml 它描述了应用的名字、版本、权限、引用的库文件等等信息,用于告诉安卓系统、手机应用市场App的信息
classes.dex java文件经jdk编译成.class文件后,由dex编译成.dex文件

用户看到一个bug,通常都是由上述某一部分导致的,因此,我们需要通过技术手段替换某些异常的文件,如资源文件、classes.dex文件、AndroidManifest.xml文件等等。

在得到一个简单的修复方向后,接下来的问题就“容易”许多了:

  • 替换谁-找出异常的文件
  • 如何替换-下发到用户手机App

笔者结合市面上所有的技术方案,将热修复技术分为三个大类:代码修复、资源修复、SO修复


图4-热修复技术方案

代码修复#

代码修复涉及到4类技术:NativeHook、Java multidex、Java Hook、Dex替换

NativeHook#

NativeHook参考阅读

NativeHook技术可以简单理解为替换虚拟机中的变量和操作,APK中的文件被安卓系统的虚拟机加载执行,我们可以通过NativeHook技术实时修改虚拟机运行中的变量和操作,从而达到修复Bug的目的。

该方案的缺点是兼容各个安卓版本、各个手机厂商的虚拟机底层实现。

Java multidex#

Java multidex参考阅读


图5-QQ空间终端开发团队-dex插桩方案

Java multidex技术简单理解是修改了类加载器加载dex文件的顺序,主要原理是

  • Hook了ClassLoader.pathList.dexElements[]。因为ClassLoader的findClass是通过遍历dexElements[]中的dex来寻找类的。当然为了支持4.x的机型,需要打包的时候进行插桩。

  • 越靠前的Dex优先被系统使用,基于类级别的修复

该方案缺点如下:

  • 下次启动才生效
  • 消耗性能,即Dalvik平台存在插桩导致的性能损耗,Art平台由于地址偏移问题导致补丁包可能过大的问题
  • 虚拟机在安装期间为类打上CLASS_ISPREVERIFIED标志是为了提高性能的,我们强制防止类被打上标志是否会影响性能?但是在大项目中拆分dex的问题已经比较严重,很多类都没有被打上这个标志。

Java Hook#

Java Hook参考阅读

Java Hook技术主要使用了反射和动态代理的Java API,修改类、方法的实现。类A、方法A指向了一个错误的实现类、实现方法等,通过JavaHook改变类A、方法A的指向,指向一个新的正常的类、方法等。

该方案缺点如下:

  • 代码是侵入式的,需要在原有的类中加入调整指向相关的代码、调整业务逻辑的代码
  • 不支持so和资源的替换
  • 显著增大apk体积,1个函数增加17.47个字节,10万个函数会增加1.67M[2]

Dex替换#

Dex替换参考阅读

Dex替换技术顾名思义,替换的是APK目录下的classes.dex文件,替换方式有全量替换和差量替换,全量替换是全量替换APK中的dex文件;差量替换是指仅替换dex文件中不一样的内容。实现该方案难度较大,建议使用Tinker实现代码修复的功能。服务端做dex差量,将差量包下发到客户端,在ART模式的机型上本地跟原apk中的classes.dex做merge,merge成为一个新的merge.dex后将merge.dex插入pathClassLoader的dexElement,原理类同Q-Zone,为了实现差量包的最小化,Tinker自研了DexDiff/DexMerge算法。Tinker还支持资源和So包的更新,So补丁包使用BsDiff来生成,资源补丁包直接使用文件md5对比来生成,针对资源比较大的(默认大于100KB属于大文件)会使用BsDiff来对文件生成差量补丁。

该方案缺点如下:

  • 替换的是APK包的dex文件,需要下一次加载APK才能生效
  • dex替换操作消耗虚拟机内存,如果遇到OOM有可能合并失败
  • 如果App本身可用内存过低,会导致App被系统杀死

综合方案#

Sophix综合了上述的方案优缺点,最终采用从虚拟机层面修改底层的ArtMethod数据结构和排列,实现了代码修复。

该方案的缺点如下:

  • 原有类结构发生变化,不适用修复
  • 反射调用的非静态方法,不适用修复

资源修复#

笔者最大的疑问:下载全量包、差量包的方式完成替换,与下载APK,并重新安装APK的做法有什么区别?

  • APK安装过程是什么?

全量包#

Amigo参考阅读

[Amigo 源码解读 - Eleme Mobilists](http://mobilists.eleme.io/2016/09/01/Amigo 源码解读/)

Amigo支持全量替换和拆分包替换,也支持即时生效和二次启动生效。

立即生效方式:

1
Amigo.work(context, apkFile);

稍后生效方式:

1
Amigo.workLater(context, apkFile, callback);

该方案缺点如下:

  • 不支持和Instant Run同时使用(或者使用autoDisableInInstantRunMode来自动停止使用amigo)

  • patch包中使用provider的方式发生了变化

    • 修改声明方式,authorities须以”${youPackageName}.provider“开头

      <provider

      android:name="me.ele.demo.provider.StudentProvider"
      android:authorities="${youPackageName}.provider.student" />
      
    • 修改调用方式

      1
      2
      3
      4
      // 1. app进程内使用时,无需做任何修改
      Cursor cursor = getContentResolver().query(Uri.parse("content://" + getPackageName() + ".provider.student?id=0"), null, null, null, null);
      // 2. 其他进程中的使用时,需要修改uri为以下形式, 其中targetPackageName为你的App的包名
      Cursor cursor = getContentResolver().query(Uri.parse("content://" + targetPackageName + ".provider/student?id=0"), null, null, null, null);
  • notification & widgetRemoteViews的自定义布局不支持修改,只支持内容修复

    • 任何使用在RemoteViews里面的资源id都需要进行这样的包装 java RCompat.getHostIdentifier(Context context, int id)

差量包#

Tinker

sophix

SO修复#

接口实现#

Tinker

插桩实现#

Amigo\sophix

参考#

Dex分包#

动态加载Dex技术

名称
Google dex分包工具multidex
美团
Androiddex分包方案
cutom class loading in dalvik

热补丁#

Tinker QZone AndFix(Sophix) Small Robust Nuwa Amigo
类替换 yes yes no no yes yes
So替换 yes no no no no yes
资源替换 yes yes no no yes yes
全平台支持 yes yes yes yes yes yes
即时生效 no no yes yes no 可选(可即时、可冷启动)
性能损耗 较小 较大 较小 较小 较大
补丁包大小 较小 较大 一般 一般 较大 很大
开发透明 yes yes no no yes yes
复杂度 较低 较低 复杂 复杂 较低 较低
gradle支持 yes no no no yes
打包方式 依赖侵入式打包 依赖侵入式打包 无侵入式打包 依赖侵入式打包 依赖侵入式打包 依赖侵入式打包 依赖侵入式打包
Rom体积 较大 较小 较小 较小 较小 较小
成功率 较高 较高 一般 最高-99% 较高 最高-100%
监控能力 提供分发控制及监控 no 提供分发控制及监控 no no no 提供分发控制及控制
开发者 腾讯团队 腾讯团队 阿里团队 个人 美团团队 大众点评团队 饿了么团队
是否开源 是,有商业版本 是,有商业版本HotFixSophix
是否收费 收费(基础版免费,但有限制) Sophix收费(设有免费阈值5万台) 免费
最近维护时间 2021年4月 2016年 2018年 2020年 2015年 2017年(被阿里收购不再维护)

热补丁原理#

image-20210603134359487

热补丁参考文章#

名称 内容大纲
Github Hotfix Github全栈Hotfix标签项目
QQ空间安卓App热补丁动态修复技术介绍 1.基于的是android dex分包方案
2.用的是在字节码插入代码,而不是源代码插入
Android热修复技术原理详解(最新最全版本) 1.什么是热修复?
2.热修复框架分类
3.技术原理及特点
4.Tinker框架解析
5.各框架对比图
6.总结
阿里《深入探索Android热修复技术原理》 阿里出版很详细
Amigo源码解读 饿了么官方出品
Android热更新方案Robust
蘑菇街Android热修复探索之路 基于 Instant Run Hot Swap
Instant Run英文原文(需要科学上网)
Aceso:美丽说蘑菇街开源的 Android 热修复方案
Android 热修复Nuwa的原理及Gradle插件源码解析
微信Android热补丁实践演进之路
Android热修复技术,你会怎么选?
Android热修复技术选型——三大流派解析 (qq.com) 插件化:一个程序划分为不同的部分,以插件的形式加载到应用中去,
本质上它使用的技术还是热修复技术,只是加入了更多工程实践,
让它支持大规模的代码更新以及资源和SO包的更新。
热修复:当线上应用出现紧急BUG,为了避免重新发版,
并且保证修复的及时性而进行的一项在线推送补丁的修复方案。。
Android Patch 方案与持续交付 腾旭buggly对tinker的支持,后台管理服务

  1. 1.深入探索Android热修复技术原理
  2. 2.Android热更新方案Robust
点击查看
-------------------本文结束 感谢您的阅读-------------------