Android热修复方案(一)理论篇

概述

热修复是我学习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
    12
    public 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框架来完成一次热修复需求。
缺点:

  1. 不能即时生效,需要下次启动,
  2. 不支持修改AndroidManifest,所以不支持新增组件(目前新版本已支持新增Activity)
  3. 不支持修改remoteView,例如桌面图标,notification icon等。

尽管Tinker有些缺点,但相较于其他的框架来说,它是目前最佳的热修复方案。目前微信使用的热修复方案就是Tinker。
具体实现细节,参看《Android热修复方案(二)实践篇
最后再多说一句,尽管可以利用热修复来解决线上的Bug,但开发过程中还是要尽量避免Bug的出现,通常产品追求的是用户体验,但对于开发者来讲,追求的是算法高效,逻辑清晰,无Bug,技术至上。