Tinker原理深入理解(二)

原理及介绍

现阶段,Android热补丁技术应该是分为以下两个流派:

  • Native:代表有阿里的Dexposed、AndFix与腾讯的内部方案KKFix;
  • Java:代表有Qzone的超级补丁、大众点评的nuwa、美团的robust、百度金融的rocooFix, 饿了么的amigo。

Native流派与Java流派都有着自己的优缺点,它们具体差异可参考此文:微信Android热补丁实践演进之路 。微信Tinker属于Java流派。核心思想是利用DexDiff算法对比差异生成Patch补丁包,分平台合成,全量替换新的Dex

微信Tinker原理图
Tinker流程图

校验Dex

Tinker它合并完成时完全使用了新的Dex,并且实现了分平台合成。这样既不出现Art地址错乱的问题,在Dalvik环境也无须插桩。当然考虑到补丁包的体积,我们不能直接将新的Dex放在里面。但可以将新旧两个Dex的差异放到补丁包中,这里主要调研的方法有以下几个:

Diff文件

1.BsDiff;它格式无关,但对Dex效果不是特别好,而且非常不稳定。当前微信对于so与部分资源,依然使用bsdiff算法;
  2.DexMerge;它主要问题在于合成时内存占用过大,一个12M的dex,峰值内存可能达到70多M;
  3.DexDiff;通过深入Dex格式,实现一套diff差异小,内存占用少以及支持增删改的算法。

DexDiff算法已经非常复杂,事实上要实现分平台合成并不容易。主要难点有以下几个方面:

  • small dex的类收集;什么类应该放在这个小的Dex中呢?
  • ClassN处理;对于ClassN怎么样处理,可能出现类从一个Dex移动到另外一个Dex?
  • 偏移二次修正; 补丁包中的操作序列如何二次修正?
  • Art.info的大小; 如何修正偏移所引入的info文件的大小?

微信团队最后实现了这一套方案,这也是其他全量合成方案所不能做到的

  • Dalvik全量合成,解决了插桩带来的性能损耗;
  • Art平台合成small dex,解决了全量合成方案占用Rom体积大, OTA升级以及Android N的问题;
  • 大部分情况下Art.info仅仅1-20K, 解决由于补丁包可能过大的问题;

事实上,DexDiff算法变的如此复杂,怎么样保证它的正确性呢?微信为此做了以下三件事情:

  • 随机组成Dex校验,覆盖大部分case;
  • 微信200个版本的随机Diff校验, 覆盖日常使用情况;
  • Dex文件合成产物有效性校验,即使算法出现问题,也只是编译不出补丁包。

每一次DexDiff算法的更新,都需要经过以上三个Test才可以提交,这样DexDiff的这套算法已完成了整个闭环。

DexDiff 算法

它属于二路归并算法,对Dexdiff算法有兴趣研究的童鞋可以看看这里:Tinker Dexdiff算法解析

算法过程描述:

1.首先我们需要将新旧内容排序,这需要针对排序的数组进行操作。
2.新旧两个指针,在内容一样的时候 old、new 指针同时加1,在 old 内容小于 new 内容(注:这里所说的内容比较是单纯的内容比较比如'A'<'a')的时候 old 指针加1 标记当前 old 项为删除。
3.在 old 内容大于 new 内容 new 指针加1, 标记当前 new 项为新增。

下面是算法执行的简单过程:

------old-----
11 foo2 
12 foo5 
13 hello dodola
14 hello dodola1
15 hello dodola2
16 hello dodola5
17 out
18 println
------new-----
11 foo3 
12 foo5 
13 hello dodola1 
14 hello dodola3
15 hello dodola_modify
16 out
17 println
对比的old cursor 和 new cursor 指针的改变以及操作判定,判定过程如下
old_11 new_11 cmp <0  del
old_12 new_11 cmp >0  add
old_12 new_12 cmp =0  no
old_13 new_13 cmp <0  del
old_14 new_13 cmp =0  no
old_15 new_14 cmp <0  del
old_16 new_14 cmp >0  add
old_16 new_15 cmp <0  del
old_17 new_15 cmp >0  add
old_17 new_16 cmp =0  no
old_18 new_17 cmp =0  no
break;
进入下一步过程
可以确定的是删除的内容肯定是从 old 中的 index 进行删除的 添加的内容肯定是从 new 中的 index 中来的,按照这个逻辑我们可以整理如下内容。
old_11 del
new_11 add
old_13 del
new_14 add
old_15 del
new_15 add
old_16 del
到这一步我们需要找出替换的内容,很明显替换的内容就是从 old 中 del 的并且在 new 中 add 的并且 index 相同的i tem,所以这就简单了
old_11 replace
old_13 del
new_14 add
old_15 replace
old_16 del
ok,到这一步我们就能判定出两个dex的变化了,很机智的算法。

运行时替换PathClassLoader

事实上,App image中的class是插入到PathClassloader中的ClassTable中。假设我们完全废弃掉PathClassloader,而采用一个新建Classloader来加载后续的所有类,即可达到将cache无用化的效果。

需要注意的问题是我们的Application类是一定会通过PathClassloader加载的,所以我们需要将Application类与我们的逻辑解耦,这里方式有两种:

1.采用类似instant run的实现;在代理application中,反射替换真正的application。这种方式的优点在于接入容易,但是这种方式无法保证兼容性,特别在反射失败的情况,是无法回退的。

2.采用代理Application实现的方法;即Application的所有实现都会被代理到其他类,Application类不会再被使用到。这种方式没有兼容性的问题,但是会带来一定的接入成本。

启动耗时对比

其他的一些问题:

由于原理与系统限制,Tinker有以下已知问题:

  • Xposed等微信插件; 市面上有各种各样的微信插件,它们在微信启动前会提前加载微信中的类,这会导致两个问题:

1.Dalvik平台:出现Class ref in pre-verified class resolved to unexpected implementation的crash;
2.Art平台:出现部分类使用了旧的代码,这可能导致补丁无效,或者地址错乱的问题。

微信在这里的处理方式是:若crash时发现安装了Xposed,则立即清除并不再应用补丁。

  • Dex反射成功但是不生效;
    部分三星android-19版本存在Dex反射成功,但出现类重复时,查找顺序始终从base.apk开始。 微信在这里的处理方式是增加Dex反射成功校验,具体通过在框架中埋入某个类的isPatch变量为false。在补丁时,我们自动将这个变量改为true。通过这个变量最终的数值,则可以知道反射成功与否。

  • 不支持部分三星android-21机型,加载补丁时会主动抛出"TinkerRuntimeException:checkDexInstall failed";

  • T不支持修改AndroidManifest.xml,Tinker不支持新增四大组件;

  • 由于Google Play的开发者条款限制,不建议在GP渠道动态更新代码;

  • 在Android N上,补丁对应用启动时间有轻微的影响;

  • 对于资源替换,不支持修改remoteView。例如transition动画,notification icon以及桌面图标。

    2017-8-4 更新 :

1.7.11版本或以下版本,项目中若使用了RxJava,在调用特殊操作符时会报如下错误:

java.lang.VerifyError: Rejecting class foc because it failed compile-time verification (declaration of 'foc' appears in /data/user/0/com.xxx.android/tinker/patch-91b25513/dex/classes5.dex.jar)
at euy.zipArray(SourceFile:4567)
    at euy.zip(SourceFile:3883)
    at euy.zipWith(SourceFile:13590)
    at aqi.c(SourceFile:67)
...

具体原因:RxJava里的zip特殊操作符逻辑加上art对aput这个指令的校验方式凑在一起,导致Crash 。Tinker 官方GitHub 项目 Issue 链接地址:https://github.com/Tencent/tinker/issues/491

参考文献:

微信移动技术团队GitHub博客地址: WeMobileDev/article
Tinker GitHub Wiki :tinker/wiki
Tinker Dexdiff算法解析 : Dexdiff算法解析
Android_N混合编译与对热补丁影响:WeMobileDev/article/Android_N

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

推荐阅读更多精彩内容