概述
热修复是我学习Android中早就定下的计划,过往的一些项目中,从来没涉及过有关热修复的需求,最多只是更新了一下.so库而已。所以今次要结合理论和实践来更加深入了解一下热修复。
《Android热修复方案(二)实践篇》
什么是热修复
定义
在已运行的项目中出现了Bug,不需要通过第二次安装就能修复系统中已知的Bug,就是热修复。
其流程为:
用户安装 ——> 发现Bug ——> 修改代码 ——> 下发补丁(热修复) ——> 系统自动拉取,修复Bug
可以看出这种技术多相对于Bug而言的话意义更大,当然也可以作为业务需求而言,例如某App的节日首页的促销活动。
优点
- 无需重新发版本
- 用户无感知修复Bug
- 修复成功率高
修复内容
- 代码修复
- 资源修复
- .so库修复
与插件化的区别
插件化是想把需要实现的模块或功能当做一个独立的提取出来,减少宿主的规模,当需要使用到相应的功能时再去加载相应的模块。
热修复原理
实现热修复的核心关键就是对代码进行修改,主要有三种方案:
- 类加载方案(关键)
在类的加载过程中会用到ClassLoader,在其中的一个环节里就是用DexPathList的findClass方法:1
2
3
4
5
6
7
8
9
10
11
12public Class<?> findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {//查找类是需要遍历Elements集合
Class<?> clazz = element.findClass(name, definingContext, suppressed);//2
if (clazz != null) {
return clazz;//如果找到目标类就直接返回,没有的话就继续遍历
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
Element是封装了DexFile,用于加载dex文件的。每个dex文件对应一个Element。
所以当类出现了Bug,我们需要将修改好的.class打包含dex文件的补丁包(patch.jar),放在Element集合的第一个位置,让其优先加载。再并到宿主的class.dex中。
这样就会首先加载我们修改好的class,根据双亲委派模型,后面存在Bug的class就不会被加载进内存中,最终实现了类的替换。
- 底层替换方案
- Instant Run方案
热修复框架
名称 | 成员 | 技术方案 |
---|---|---|
阿里系 | AndFix、Dexposed、阿里百川、Sophix | 底层替换方案 |
腾讯系 | 微信的Tinker、QQ空间的超级补丁、手机QQ的QFix | 类加载方案 |
知名公司 | 饿了么的Amigo | 类加载方案 |
知名公司 | 美团的Robust、蘑菇街的Aceso | Instant Run方案 |
Tinker
接下来将基于Tinker框架来完成一次热修复需求。
缺点:
- 不能即时生效,需要下次启动,
- 不支持修改AndroidManifest,所以不支持新增组件(目前新版本已支持新增Activity)
- 不支持修改remoteView,例如桌面图标,notification icon等。
尽管Tinker有些缺点,但相较于其他的框架来说,它是目前最佳的热修复方案。目前微信使用的热修复方案就是Tinker。
具体实现细节,参看《Android热修复方案(二)实践篇》
最后再多说一句,尽管可以利用热修复来解决线上的Bug,但开发过程中还是要尽量避免Bug的出现,通常产品追求的是用户体验,但对于开发者来讲,追求的是算法高效,逻辑清晰,无Bug,技术至上。