深入探索Android热修复技术原理读书笔记 —— 热修复技术介绍
1.1 什么是热修复
对于广大的移动开发者而言,发版更新是最为寻常不过的事了。然而,如果你 发现刚发出去的包有紧急的BUG需要修复,那你就必须需要经过下面这样的流程:
这就是传统的更新流程,步骤十分繁琐。总的来说,传统流程存在这几大弊端:
重新发布版本代价太大
用户下载安装成本太高
BUG 修复不及时,用户体验太差
相应的,许多开发者找到了比较合适的解决办法。
Hybrid 方案。也就是把需要经常变更的业务逻辑以 H5 的方式独立出来。而这种方案, 需要传统的 java 开发者学习前端语言,不仅增加了学习成本,而且还要对原先的逻辑 进行合适的抽象和转换。并且,对于无法转为 H5 形式的代码仍旧是无法修复的。
使用插件化方案来解决问题,像 Atlas 或者 DroidPlugin 方案。 而这类方式,移植成本非常高,还要学习整套插件化工具,对原先老代码的改造。
于是,热修复技术应运而生了。
1.2 技术沉淀
阿里系:
Dexposed:基于Xposed改进,针对Android Dalvik虚拟机运行的Java Method Hook技术,但无法兼容Android5.0以后的虚拟机
Andfix:也是一种底层替换的方案,做到了 Dalvik 和 ART 的兼容
Hotfix:结合实际工程中的使用Andfix的经验,推出阿里百川Hotfix,但只提供了代码层面的修复,对于资源和so的修复还未实现
Sophix:2017年6月推出Sophix,打破了各家纷争的局面,在代码修复,资源修复,so修复方面,都做到了业界领先
其他著名的热修复,但是各自有各自的局限性,补丁过大,效率低下,不够稳定,用起来繁琐:
腾讯 QQ 空间的超级补丁
微信的 Tinker
饿了么的 Amigo
美团的 Robust
1.3 详细比较
Sophix和Tinker与Amigo的比较:
各项指标都占优,唯一不支持的就是四大组件的修复
1.4 技术概览
1.4.1 设计理念
Sophix 的设计理念,就是非侵入性
最终的实现只有两个生成的新旧 apk,唯一要做的就是初始化和请求补丁两行代码
不会侵入 apk 的 build 流程中
不改变任何打包组件
不插入任何 AOP 代码
1.4.2 代码修复
代码修复有两大主要方案,一种是阿里系的底层替换方案,另一种是腾讯系的类加载方案。
两种方案各有优劣:
底层替换方案限制颇多,但时效性最好,加载轻快,立即见效。
类加载方案时效性差,需要重新冷启动才能见效,但修复范围广,限制少。
底层替换方案
底层替换方案是在已经加载了的类中直接替换掉原有方法,是在原来类的基础上进行修改的。因而无法实现对与原有类进行方法和字段的增减,因为这样将破坏原有类的结构。
一旦补丁类中出现了方法的增加和减少,就会导致这个类以及整个 Dex 的方法数的变化。方法数的变化伴随着方法索引的变化,这样在访问方法时就无法正常地索引到正确的方法了。如果字段发生了增加和减少,和方法变化的情况一样,所有字段 的索引都会发生变化。并且更严重的问题是,如果在程序运行中间某个类突然增加了 —个字段,那么对于原先已经产生的这个类的实例,它们还是原来的结构,这是无法改变的。而新方法使用到这些老的实例对象时,访问新增字段就会产生不可预期 的结果。
这是这类方案的固有限制,而底层替换方案最为人诟病的地方,在于底层替换的不稳定性。
通过对代码的底层替换原理重新进行了深入思考,从克服其限制和兼容性入 手,以一种更加优雅的替换思路,实现了即时生效的代码热修复。
采用一种无视底层具体结构的替换方式,这种方式不仅解决了兼容性问题,并且由于忽略了底层ArtMethod结构的差异,对于所有的Android版本都不 再需要区分,代码量大大减少。即使以后的Android版本不断修改ArtMethod的 成员,只要保证ArtMethod数组仍是以线性结构排列,就能直接适用于将来的 Android 8.0、9.0等新版本,无需再针对新的系统版本进行适配了。
类加载方案
类加载方案的原理是在 app 重新启动后让 Classloader 去加载新的类。因为在 app运行到一半的时候,所有需要发生变更的类已经被加载过了,在 Android 上是无法对一个类进行卸载的。如果不重启,原来的类还在虚拟机中,就无法加载新类。 因此,只有在下次重启的时候,在还没走到业务逻辑之前抢先加载补丁中的新类,这样后续访问这个类时,就会Resolve为新类。从而达到热修复的目的。
再来看看腾讯系三大类加载方案的实现原理。
QQ 空间方案会侵入打包流程,并 且为了 hack 添加一些无用的信息,实现起来很不优雅。
QFix 的方案,需要获取 底层虚拟机的函数,不够稳定可靠,并且有个比较大的问题是无法新增public函数。
微信的 Tinker 方案是完整的全量 dex 加载,并且可谓是将补丁合成做到了极致。Tinker 的合成方案,是从 dex 的方法和指令维度进行全量合成,整个过程都是自己研发的。虽然可以很大地节省空间,但由于对dex内容的比较粒度过细,实现较为复杂,性能消耗比较严重。实际 上,dex 的大小占整个apk的比例是比较低的,一个 app 里面的dex文件大小并不 是主要部分,而占空间大的主要还是资源文件。因此,Tinker 方案的时空代价转换的性价比不高。
dex 比较的最佳粒度,应该是在类的维度。它既不像方法和指令维度那样的细微,也不像 bsbiff 比较那般的粗糙。在类的维度,可以达到时间和空间平衡的最 佳效果。基于这个准则,另辟蹊径,实现了一种完全不同的全量dex替换方案。
直接利用 Android 原先的类查找和合成机制,快速合成新的全量 dex。这么一来,既不需要处理合成时方法数超过的情况,对于 dex 的结构也不用进行破坏性重构。
重新编排了包中dex的顺序。虚拟机查找类的时候,会优先找到 classes.dex 中的类,然后才是 classes2.dex、classes3.dex,也可以看做是 dex 文件级别的类插桩方案。这个方式对旧包与补丁包中 classes.dex 的顺 序进行了打破与重组,最终使得系统可以自然地识别到这个顺序,以实现类覆盖的目 的。大大减少合成补丁的开销。
双剑合璧
既然底层替换方案和类加载方案各有其优点,把他们联合起来不是最好的选择吗?
Sophix的代码修复体系正是同时涵盖了这两种方案。两种方案的结合,可以实现优势互补,完全兼顾的作用,可以灵活地根据实际情况自动切换。
在补丁生成阶段,补丁工具会根据实际代码变动情况进行自动选择,
针对小修改,在底层替换方案限制范围内的,就直接采用底层替换修复吗,这样可以做到代码修复即时生效。
对于代码修改超出底层替换限制的,会使用类加载替换,这样虽然及时性没那么好,但总归可以达到热修复的目的。
运行时阶段,Sophix 还会再判断所运行的机型是否支持热修复,这样即使补丁支持热修复,但由于机型底层虚拟机构造不支持,还是会走类加载修复,从而达到最好的兼容性。
1.4.3 资源修复
目前市面上的很多资源热修复方案基本上都是参考了 Instant Run 的实现。实际 上,Instant Run 的推出正是推动这次热修复浪潮的主因,各家热修复方案,在代码、 资源等方面的实现,很大程度上地参考了 Instant Run的代码,而资源修复方案正是 被拿来用到最多的地方。
简要说来,Instant Run 中的资源热修复分为两步:
构造一个新的AssetManager,并通过反射调用 addAssetPath,把这个完 整的新资源包加入到 AssetManager 中。这样就得到了一个含有所有新资源 的 AssetManager。
找到所有之前引用到原有AssetManager的地方,通过反射,把引用处替换为 AssetManager 。
新的实现方式:构造了一个 package id 为 0x66 的资源包,这个包里只包含改变了的资源项,然后直接在原有 AssetManager 中 addAssetPath 这个包就可以了。由于补丁包的 package id 为 0x66,不与目前已经加载的 0x7f 冲突,因此直接加入到已有的 AssetManager 中就可以直接使用了。
补丁包里面的资源,只包含原有包里面没有而新的包里面有的新增资源,以及原有内容发生了改变的资源。并且,我们采用了更加优雅的替换方式,直接在原有的 AssetManager 对象上进行析构和重构,这样所有原先对 AssetManager对象的引用是没有发生改变的,所以就不需要像Instant Run 那样 进行繁琐的修改了。
可以说,我们的资源修复方案,优越性超过了 Google官方的Instant Run方 案。整个资源替换的方案优势在于:
不修改 AssetManager 的引用处,替换更快更完全。(对比 Instanat Run 以 及所有 copycat 的实现)
不必下发完整包,补丁包中只包含有变动的资源。(对比 Instanat Runs Amigo 等方式的实现)
不需要在运行时合成完整包。不占用运行时计算和内存资源。(对比 Tinker 的实现)
1.4.4 SO库修复
SO 库的修复本质上是对 native 方法的修复和替换。
我们采用的是类似类修复反射注入方式。把补丁 so 库的路径插入到 nativeLi- braryDirectories 数组的最前面,就能够达到加载 so 库的时候是补丁 so 库,而不是原来 so 库的目录,从而达到修复的目的。
采用这种方案,完全由 Sophix 在启动期间反射注入 patch 中的 so 库。对开发者依然是透明的。不用像某些其他方案需要手动替换系统的 System.load 来实现替 换目的。
1.5 本章小结
本章介绍了热修复技术的主要使用场景和为业界带来的变化。详细说明了阿里巴巴推出的热修复解决方案 Sophix 的由来,同时与其他各大主流方案进行了比较。另外,粗略介绍了热修复所涉及的各个方面,并引导概述后续各个章节。
参考文章
深入探索Android热修复技术原理读书笔记——第一章:热修复技术介绍
深入探索Android热修复技术原理[book]
深入探索Android热修复技术原理读书笔记 —— 热修复技术介绍的更多相关文章
- 深入探索Android热修复技术原理读书笔记 —— 代码热修复技术
在前一篇文章 深入探索Android热修复技术原理读书笔记 -- 热修复技术介绍中,对热修复技术进行了介绍,下面将详细介绍其中的代码修复技术. 1 底层热替换原理 在各种 Android 热修复方案中 ...
- 深入探索Android热修复技术原理读书笔记 —— 资源热修复技术
该系列文章: 深入探索Android热修复技术原理读书笔记 -- 热修复技术介绍 深入探索Android热修复技术原理读书笔记 -- 代码热修复技术 1 普遍的实现方式 Android资源的热修复,就 ...
- 深入探索Android热修复技术原理读书笔记 —— so库热修复技术
热修复系列文章: 深入探索Android热修复技术原理读书笔记 -- 热修复技术介绍 深入探索Android热修复技术原理读书笔记 -- 代码热修复技术 深入探索Android热修复技术原理读书笔记 ...
- <<操作系统精髓与设计原理>>读书笔记(一) 并发性:互斥与同步(1)
<<操作系统精髓与设计原理>>读书笔记(一) 并发性:互斥与同步 并发问题是所有问题的基础,也是操作系统设计的基础.并发包括很多设计问题,其中有进程间通信,资源共享与竞争,多个 ...
- Struts2技术内幕 读书笔记一 框架的本质
本读书笔记系列,主要针对陆舟所著<<Struts2技术内幕 深入解析Strtus2架构设计与实现原理>>一书.笔记中所用的图片若无特殊说明,就都取自书中,特此声明. 什么是框架 ...
- 《Android源代码设计模式解析》读书笔记——Android中你应该知道的设计模式
断断续续的,<Android源代码设计模式解析>也看了一遍.书中提到了非常多的设计模式.可是有部分在开发中见到的几率非常小,所以掌握不了也没有太大影响. 我认为这本书的最大价值有两点,一个 ...
- LOMA280保险原理读书笔记
LOMA是国际金融保险管理学院(Life Office Management Association)的英文简称.国际金融保险管理学院是一个保险和金融服务机构的国际组织,它的创建目的是为了促进信息交流 ...
- Android驱动开发5-8章读书笔记
Android驱动开发读书笔记 第五章 S5PV210是一款32位处理器,具有 ...
- Struts2技术内幕 读书笔记三 表示层的困惑
表示层能有什么疑惑?很简单,我们暂时忘记所有的框架,就写一个注册的servlet来看看. index.jsp <form id="form1" name="form ...
随机推荐
- JUnit5学习之八:综合进阶(终篇)
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- springboot源码(4)
我们上3个篇章写了springboot的自动装配.servlet组件的注入以及web容器实现内嵌的原理,现在我们来看springboot启动过程中到底做了些什么,也就是打开我们的run方法: 这里我们 ...
- go-优雅地关机或重启
目录 优雅地关机或重启 优雅地关机 什么是优雅关机? 如何实现优雅关机? 优雅地重启 总结 优雅地关机或重启 我们编写的Web项目部署之后,经常会因为需要进行配置变更或功能迭代而重启服务,单纯的kil ...
- Django 页面缓存的cache_key是如何生成的
页面缓存 e.g. @cache_page(time_out, key_prefix=key_prefix) def my_view(): ... 默认情况下,将使用配置中的default cache ...
- 从sql语句的角度解刨SqlServer插入语句的并发问题
今天收到一个小学弟的求助,数据库插入偶尔重复,怎么在sql语句上进行解决. Q:学长,我导入excel数据的操作,平时使用好好的,怎么突然发生插入重复的问题? A:你是使用哪个ORM框架进行操作的? ...
- 安全框架Drozer安装和简单使用
安全框架Drozer安装和简单使用 说明: drozer(即以前的Mercury)是一个开源的Android安全测试框架 drozer不是什么新工具,但确实很实用,网上的资料教程都很多了,最近自己项目 ...
- golang io操作之写篇
/** * @author livalon * @data 2018/9/4 15:11 */ package main import ( "os" "fmt" ...
- 获得PyInstaller打包exe的py源码
参考链接:https://laucyun.com/33359ed9f725529ac9b606d054c8459d.html way1:pyi-archive_viewer 提取pyc,uncomp ...
- 攻防世界 reverse babymips
babymips XCTF 4th-QCTF-2018 mips,ida中想要反编译的化需要安装插件,这题并不复杂直接看mips汇编也没什么难度,这里我用了ghidra,直接可以查看反编译. 1 ...
- 对接快递100&聚水潭API
对接快递100&聚水潭API 入我相思门,知我相思苦. 简介:对接第三方平台快递100&聚水潭API的简要总结. 1.感悟 个人感觉快递100的API更友好一些,比如有SDK可以调用: ...