热修复框架对比(Tinker、Robust)

一、框架特性对比

特性 Tinker Robust
即时生效
方法替换
类替换
类结构修改
资源替换
so替换
支持gradle
补丁包大小 较小 一般
Rom体积 较大 较小
成功率 较高 最高

二、补丁包生成对比

1、Tinker

Tinker支持命令行工具和Gradle两种打包方式,更推荐gradle的方式。

1.1、命令行工具生成

命令行工具tinker-patch-cli.jar提供了基准包与新安装包做差异,生成补丁包的功能。具体的命令参数如下:

java -jar tinker-patch-cli.jar -old old.apk -new new.apk -config tinker_config.xml -out output_path

参数与gradle基本一致,新增的sign参数,需要输入签名路径与签名信息。在编译时需要将TINKER_ID插入到AndroidManifest.xml中。例如

<meta-data android:name="TINKER_ID" android:value="tinker_id_b168b32"/>

1.2、Gradle生成

调用assembleDebug编译,生成的基准包保存在build/bakApk中。

bakApk.png

然后修改要修复的代码,生成补丁包前需要修改build.gradle中的三个参数

  • oldApk:基准APK文件
  • applyMapping:基准包的 Proguard mapping.txt 文件路径
  • applyResourceMapping:基准包的资源 R.txt 文件路径

调用tinkerPatchDebug, 补丁包与相关日志会保存在/build/outputs/tinkerPatch/。然后将补丁包patch_signed_7zip.apk推送到手机的sdcard中就可以修复Bug了。

tinkerPatchTask.png

2、Robust

自动化补丁工具

打补丁包前需要保存好mappingmethodsMap.robust文件。打补丁包时,将之前保存的这两份文件放置在app/robus/目录下以供Robust去对比两个版本的差异来生成补丁。

mapping与methodsMap.robust文件.png

在App的build.gradle下将之前注释掉的’auto-patch-plugin’插件给打开,然后就可以修改代码了。

apply plugin: 'com.android.application'
//制作补丁时将下面这个apply打开,auto-patch-plugin紧跟着com.android.application
//apply plugin: 'auto-patch-plugin'
apply plugin: 'robust'

需要改动的方法上面添加@Modify注解,对于Lambda表达式,则需要在修改过的方法里面调用RobustModify.modify()方法。

修改方法前
public void loadData() {
    Log.d("修改方法前");
}
修改方法后
@Modify
public void loadData() {
    Log.d("修改方法后");
}
新增方法
@Add
public void add() {
    Log.d("新增方法");
}

改完代码后,运行和生成线上apk同样的命令,即可生成补丁,补丁目录app/build/outputs/robust/patch.jar,推送到手机的sdcard中就可以修复Bug了。

三、代码修复原理对比

目前有三种修复方案,类加载方案、Instant Run方案和底层替换方案。Tinker使用的是类加载方案,而Robust使用的则是Instant Run的方式。

1、类加载方案(Tinker)

ClassLoader在加载类的过程中,会调用DexPathList的findClass方法,Element中封装了DexFile用于加载Dex文件。

public Class<?> findClass(String name, List<Throwable> suppressed) {
        for (Element element : dexElements) {
            Class<?> clazz = element.findClass(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }
        if (dexElementsSuppressedExceptions != null) {
            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
        }
        return null;
    }

当查找class时,遍历dexElements有序数组,通过Element查找到Class,查找到后返回,没有查找到会接着在下一个Element中查找。修改一个有bug的类(Key.class)后,将类(Key.class)打包成包含dex的补丁包Patch.jar放到dexElements数组的第一个元素,当查找类时首先会找到Patch.dex中修改后的类(Key.class),由于ClassLoader的双亲委托模式,将不会再加载后面有bug的类(Key.class),如下图:

ClassLoader查找流程.png

Tinker将新旧APK包做差异生成patch.dex,将patch.dex与apk中的classes.dex合并,再将新的classes.dex放到dexElements数组的第一个元素,以此来替换有bug的类。由于加载后的类是无法删除的,如果重新加载新的类需要重新启动App,所以这种方法无法即时生效。

2、Instant Run方案(Robust)

Robust会在每个类中注入一个静态变量,在方法前插入一段逻辑控制代码。

注入前
public long getIndex() {
      return 100;
 }
注入后
public static ChangeQuickRedirect changeQuickRedirect;
public long getIndex() {
     if(changeQuickRedirect != null) {
         //PatchProxy中封装了获取当前className和methodName的逻辑,并在其内部最终调用了changeQuickRedirect的对应函数
         if(PatchProxy.isSupport(new Object[0], this, changeQuickRedirect, false)) {
             return ((Long)PatchProxy.accessDispatch(new Object[0], this, changeQuickRedirect, false)).longValue();
         }
     }
     return 100L;
 }

获取到补丁后,会创建DexClassLoader加载补丁,通过被修复的类信息找到该类,反射将changeQuickRedirect对象赋值为补丁对象,changeQuickRedirect将不为空,执行到方法时就会调用补丁的方法而不是原方法的逻辑,这样就起到了热修复的目的。

补丁加载.png

四、注意事项

1、Tinker

  • Tinker不支持修改AndroidManifest.xml,Tinker不支持新增四大组件(1.9.0支持新增非export的Activity)
  • 由于Google Play的开发者条款限制,不建议在GP渠道动态更新代码
  • 在Android N上,补丁对应用启动时间有轻微的影响
  • 不支持部分三星android-21机型,加载补丁时会主动抛出"TinkerRuntimeException:checkDexInstall failed"
  • 对于资源替换,不支持修改remoteView。例如transition动画,notification icon以及桌面图标

2、Robust

  • 内部类的构造方法是private(private会生成一个匿名的构造函数)时,需要在制作补丁过程中手动修改构造方法的访问域为public
  • 对于方法的返回值是this的情况现在支持不好,比如builder模式,但在制作补丁代码时,可以通过如下方式来解决,增加一个类来包装一下(如下面的B类),
method a(){
  return this;
}

改为

method a(){
  return new B().setThis(this).getThis();
}
  • 字段增加能力内测中,不过暂时可以通过增加新类,把字段放到新类中的方式来实现字段增加能力
  • 新增的类支持包括静态内部类和非内部类
  • 对于只有字段访问的函数无法直接修复,可通过调用处间接修复
  • 构造方法的修复内测中
  • 资源和so的修复内测中

参考文章:
Tinker wiki
Robust wiki
Android热修复原理
Android热更新方案Robust
美团Robust原理解析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341