昨天难得秀了一把我跟我儿子的照片,后台被刷爆了...

写一篇原创文章很简单,但是坚持原创真心很难,好在我能顺带赚点奶粉钱,这是我能坚持下去的很大原因,所以昨天主要是感谢大家一直以来对我的支持,尤其是那些赞赏或者点广告的同学,你们很良心,昨天的福利真心很难得,因为我几乎很少在公开场合爆照,且看且珍惜!

今天来给大家上一篇干货,近两年 Android 的热补丁修复技术被大家所关注,各大公司也争相开源了自己的热修复项目,而微信毫无疑问在国内不管是产品还是技术都备受瞩目,Tinker 是微信内部所使用的热补丁修复项目,项目的主要作者张绍文在昨天的 MDCC 大会上也对 Tinker 做了一个系统的介绍,紧接着大会之后微信宣布对 Tinker 进行开源,是 GitHub 上 Tencent 公司的第一个开源项目,这无疑对开源社区,尤其是 Android 界是最好的消息。我跟绍文在微信上交流过,其实也第一时间知道大会之后 Tinker 会开源的消息,绍文也找到我希望让更多的人知道,这种造福大家的大好事必须啊,所以以下是经过微信技术团队公号(WeMobileDev)授权发布的信息,原文如下:

最近半年以来,Android热补丁技术热潮继续爆发,各大公司相继推出自己的开源框架。Tinker在最近也顺利完成了公司的审核,并非常荣幸的成为github.com/Tencent上第一个正式公开的项目。

回顾这半年多的历程,这是一条跪着走完,坑坑不息之路。或许只有自己真正经历过,深入研究过, 才会真正的明白

热补丁不是请客吃饭

对热补丁技术本身,还是对使用者来说都是如此。我希望通过分享微信在这历程中的思考与经验,能帮助大家更容易的决定是否在自己的项目中使用热补丁技术,以及选择什么样方案。

热补丁技术背景

热补丁是什么以及它的应用场景介绍,大家可以参考文章「微信Android热补丁实践演进之路」。

在笔者看来Android热补丁技术应该分为以下两个流派:

  • Native,代表有阿里的Dexposed、AndFix与腾讯的内部方案KKFix;

  • Java,代表有Qzone的超级补丁、大众点评的nuwa、百度金融的rocooFix, 饿了么的amigo以及美团的robust。

Native流派与Java流派都有着自己的优缺点,它们具体差异大家可参考上文。事实上从来都没有最好的方案,只有最适合自己的。

对于微信来说,我们希望得到一个“高可用”的补丁框架,它应该满足以下几个条件:

  1. 稳定性与兼容性;微信需要在数亿台设备上运行,即使补丁框架带来1%的异常,也将影响到数万用户。

  2. 性能;补丁框架不能影响应用的性能,这里基于大部分情况下用户不会使用到补丁。其次补丁包应该尽量少,这关系到用户流量与补丁的成功率问题;

  3. 易用性;我们同时希望补丁框架简单易用,并且可以全面支持,甚至可以做到功能发布级别。

在“高可用”这个大前提下,微信对当时存在的两个方案做了大量的研究:

  1. Dexposed/AndFix;最大挑战在于稳定性与兼容性,而且native异常排查难度更高。另一方面,由于无法增加变量与类等限制,无法做到功能发布级别;

  2. Qzone;最大挑战在于性能,即Dalvik平台存在插桩导致的性能损耗,Art平台由于地址偏移问题导致补丁包可能过大的问题;

在2016年3月,微信为了追寻“高可用”这个目标,决定尝试搭建自己的补丁框架—Tinker。

Tinker框架的演绎并不是一蹴而就,它大致分为三个阶段,每一阶段需要解决的核心问题并不相同。而Tinker v1.0的核心问题是实现符合性能要求的Dex补丁框架。

Tinker v1.0—性能极致追求之路

为了稳定性与兼容性,微信选择了Java流派。当前最大难点在于如何突破Qzone方案的性能问题,通过研究Instant Run的冷插拔与buck的exopackage给了我们灵感。它们的思想都是全量替换新的Dex。

简单来说,我们通过完全使用了新的Dex,那样既不出现Art地址错乱的问题,在Dalvik也无须插桩。当然考虑到补丁包的体积,我们不能直接将新的Dex放在里面。但我们可以将新旧两个Dex的差异放到补丁包中,这里我们可以调研的方法有以下几个:

  1. BsDiff;它格式无关,但对Dex效果不是特别好,而且生成产物大小非常不稳定。当前微信对于so与部分资源,依然使用bsdiff算法;

  2. DexMerge;它主要问题在于合成时内存占用过大,一个12M的dex,峰值内存可能达到70多M;

  3. DexDiff;通过深入Dex格式,实现一套生成产物小,内存占用少以及支持增删改的算法。

如何选择?在“高可用”的核心诉求下,性能问题也尤为重要。非常庆幸微信在当时那个节点坚决的选择了自研DexDiff算法,这过程虽然有苦有泪,但也正是有它,才有现在的Tinker。

一. DexDiff技术实践

在不断的深入研究究Dex格式后,我们发现自己跳进了一个深坑,主要难点有以下三个:

  1. Dex格式复杂;Dex大致分为像StringID,TypeID这些Index区域以及使用Offset的Data区域。它们有大量的互相引用,一个小小的改变可能导致大量的Index与Offset变化;

  2. dex2opt与dex2oat校验;在这两个过程系统会做例如四字节对齐,部分元素排序等校验,例如StringID按照内容的Unicode排序,TypeID按照StringID排序...

  3. 低内存,快速;这要求我们对Dex每一块做到一次读写,无法像baksmali与dexmerge那样完全结构化。

下面以最简单的Index区域举例:

  1. Del 2;"b"元素被删除,它对应的Index是2,为了减少补丁包体积,除了新增的元素其他一律只存Index;

  2. "c", "d", "e"元素自动前移,无须操作;

  3. Addf(5); 在第五个位置增加"f"这个元素。

对于Offset区,由于每个Section可能有非常多的元素,这里会更加复杂。最后我们得到最终的操作队列,为什么DexDiff可以做到内存非常少?这是因为DexDiff算法是每一个操作的处理,它无需一次性读入所有的数据。DexDiff的各项数据如下:

二. Android N的挑战

信心满满上线后,却很快收到华为反馈的一个Crash:

在之前的基础上,这一块的研究并没有花太多的时间,主要是Android N的混合编译模式导致。更多的详细分析可参考文章「Android N混合编译与对热补丁影响解析」。

三. 厂商OTA的挑战

刚刚解决完Android N的问题,还在沉醉在自己的胜利的愉悦中。前线很快又传来噩耗,小米反馈开发版的一些用户在微信启动时黑屏,甚至ANR.

当时第一反应是不可能,所有的DexOpt操作都是放到单独的进程,为什么只在Art平台出现?为什么小米开发版用户反馈比较多?经过分析,我们发现优化后odex文件存在有效性的检查:

  • Dalvik平台:modtime/crc...

  • Art平台: checksum/image_checksum/image_offset...

这就非常好理解了,因为OTA之后系统image改变了,odex文件用到image的偏移地址很可能已经错误。对于ClassN.dex文件,在OTA升级系统已完成重新dex2oat,而补丁是动态加载的,只能在第一次执行时同步执行。

这个耗时可能高达十几秒,黑屏甚至ANR也是非常好理解。那为什么只有小米用户反馈比较多呢?这也是因为小米开发版每周都会推送系统升级的原因。

在当时那个节点上,我们重新的审视了全量合成这一思路,再次对方案原理本身产生怀疑,它在Art平台上面带来了以下几个代价:

  1. OTA后黑屏问题;这里或许可以通过loading界面实现,但并不是很好的方案;

  2. Rom体积问题;一个10M的Dex,在Dalvik下odex产物只有11M左右,但在Art平台,可以达到30多M;

  3. Android N的问题;Android N在混合编译上努力,被补丁全量合成机制所废弃了。这是因为动态加载的Dex,依然是全量编译。

回想起来,Qzone方案它只把需要的类打包成补丁推送,在Art平台上可能导致补丁很大,但它肯定比全量合成的Dex少很多很多。在此我们提出分平台合成的想法,即在Dalvik平台合成全量Dex,在Art平台合成需要的小Dex。

DexDiff算法已经非常复杂,事实上要实现分平台合成更加不容易。

  • small dex的类收集;什么类应该放在这个小的Dex中呢?

  • ClassN处理;对于ClassN怎么样处理,可能出现类从一个Dex移动到另外一个Dex?

  • 偏移二次修正; 补丁包中的操作序列如何二次修正?

  • Art.info的大小; 为了修正偏移所引入的info文件的大小?

庆幸的是,面对困难我们并没有畏惧,最后实现了这一套方案,这也是其他全量合成方案所不能做到的:

  1. Dalvik全量合成,解决了插桩带来的性能损耗;

  2. Art平台合成small dex,解决了全量合成方案占用Rom体积大, OTA升级以及Android N的问题;

  3. 大部分情况下Art.info仅仅1-20K, 解决由于补丁包可能过大的问题;

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

  1. 随机组成Dex校验,覆盖大部分case;

  2. 微信200个版本的随机Diff校验, 覆盖日常使用情况;

  3. Dex文件合成产物有效性校验,即使算法出现问题,也只是编译不出补丁包。

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

四. 其他技术挑战

在实现过程,我们还发现其他的一些问题:

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

  1. Dalvik平台:出现Class ref in pre-verified class resolved to unexpected  implementation的crash;

  2. Art平台:出现部分类使用了旧的代码,这可能导致补丁无效,或者地址错乱的问题。

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

2.  Dex反射成功但是不生效;部分三星android-19版本存在Dex反射成功,但      出现类重复时,查找顺序始终从base.apk开始。

微信在这里的处理方式是增加Dex反射成功校验,具体通过在框架中埋入某个类的isPatch变量为false。在补丁时,我们自动将这个变量改为true。通过这个变量最终的数值,我们可以知道反射成功与否。

Tinker v1.0总结

一. 关于性能

通过Tinker v1,0的努力,我们解决了Qzone方案的性能问题,得到一个符合“高可用”性能要求的补丁框架。

  • 它补丁包大小非常少,通常都是10k以内;

  • 对性能几乎没有影响,2%的性能影响主要原因是微信运行时校验补丁Dex文件的md5导致(虽然文件在/data/data/目录,微信为了更高级别的安全);

  • Art平台通过革命性的分平台合成,既解决了地址偏移的问题,占Rom体积与Qzone方案一致。

二. 关于成功率

也许有人会质疑微信成功率为什么这么低,其他方案都是99%以上。事实上,我们的成功率计算方式是:

应用成功率= 补丁版本转化人数/基准版本安装人数

即三天后,94.1%的基础版本都成功升级到补丁版本,由于基础版本人数也是持续增长,同时可能存在基准或补丁版本用户安装了其他版本,所以本统计结果应略为偏低,但它能现实的反应补丁的线上总体覆盖情况。

事实上,采用Qzone方案,3天的成功率大约为96.3%,这里还是有很多的优化空间。

三. Tinker v2.0-稳定性的探寻之路

在v1.0阶段,大部分的异常都是通过厂商反馈而来,Tinker并没有解决“高可用”下最核心的稳定性与兼容性问题。

我们需要建立完整的监控与补丁回退机制,监控每一个阶段的异常情况。这也是Tinker v2.0的核心任务,由于边幅问题这部分内容将放在下一篇文章。


关注Tinker,来Github给我们star吧

https://github.com/Tencent/tinker

查看Tinker项目源码,请点击「阅读原文」。

本文由“135编辑器”提供技术支持

掘金是一个高质量的技术社区,从 RxJava 到 React Native,性能优化到优秀开源库,让你不错过 Android 开发的每一个技术干货。长按图片二维码识别或者各大应用市场搜索「掘金」,技术干货尽在掌握中。

大腾讯的第一个开源项目「Tinker」相关推荐

  1. 腾讯首款元宇宙项目「ZPLAN」曝光,由天美工作室群总裁姚晓光带队

    9 月 29 日,腾讯游戏天美工作室发布了数条关于新项目「ZPLAN」的招聘信息.据知情人士透露,此项目由腾讯副总裁.天美工作室群总裁姚晓光亲自带队,团队规模有千人以上,且仍在扩张中.「ZPLAN」将 ...

  2. php顶级框架,10个顶级PHP开源项目「2019」

    1.Laravel Laravel是一个为Web开发者打造的PHP开发框架. GitHub Stars: 43.5k+ 网址:https://github.com/laravel/laravel 2. ...

  3. 如何开始第一个开源项目?

    作者 | Mahdhi Rezvi 译者 | 苏本如,责编 | 伍杏玲 出品 | CSDN(ID:CSDNnews) 以下为译文: 根据Sayan Chowdhury的说法,以下是人们开始开源之旅时提 ...

  4. 【Python数据科学 | 11】应用实战:我的第一个开源项目-基金定投回测工具

    这是机器未来的第60篇文章 原文首发地址:https://robotsfutures.blog.csdn.net/article/details/127712752 <Python数据科学快速入 ...

  5. 【音视频开发系列】一学就会,快速掌握音视频开发的第一个开源项目FFmpeg

    快速掌握音视频开发的第一个开源项目:FFmpeg 1.为什么要学FFmpeg 2.FFmpeg面向对象思想分析 3.FFmpeg各种组件剖析 视频讲解如下,点击观看: [音视频开发系列]一学就会,快速 ...

  6. 开源的「变」与「恒」:七大开源基金会负责人尖峰对谈

    在 7 月 9 日举办的首届全球开源技术峰会 GOTC 2021 上海站,来自全球各顶级基金会的负责人共聚圆桌,围绕开源软件的历史与未来展开了深刻的探讨. Linux 基金会执行董事 Jim Zeml ...

  7. Android 手机运存越来越大,为什么后台应用还是会被「杀」?

    不知不觉间 Android 陷入了一个关于「后台」的怪圈:一边各大厂商陆续推出了 12G RAM 的手机,另一边你刚刚放到后台的下载任务没有如预期那样后台挂机下载,打开微信发现还得陪启动画面的孤独小人 ...

  8. 腾讯第100个开源项目:微信开源推理加速工具TurboTransformers

    出品 | AI科技大本营(ID:rgznai100) 4月24日,腾讯正式宣布开源Transformer推理加速工具TurboTransformers.该工具面向自然语言处理领域中Transforme ...

  9. 来自腾讯的76款开源项目

    腾讯作为互联网行业的一大巨头,一直都不吝啬将好的技术开放,与广大开发者共享,未来也将推出VR开放平台和AI技术.腾讯在全球合作伙伴大会披露了未来五年的发展思路,在技术共享方面,希望利用自己在新技术方面 ...

  10. python读json文件太大github_GitHub上最火的开源项目是啥|JSON文件实战处理

    Python常见的数据文件格式处理有4种(CSV,JSON,XML.HTML),我们前面已经讲了CSV文件的处理,今天我们来讲一下JSON文件的处理,最后用一个实战小例子,来分析一下程序员中的face ...

最新文章

  1. DataBase 之 拉链表结构设计
  2. html手机端适配怎么调试,html5面试常见问题及答案:移动端布局与适配篇
  3. Facebook数字货币凸显硅谷雄心:打造全球金融操作系统
  4. 公安网安装mysql 5.7_安装Mysql 5.7.1
  5. 梯度下降法进行线性回归---------二维及多维
  6. 计算机基础知识二进制转换,计算机基础知识数制转换
  7. QT+VS中ui不能声明为指针?
  8. linux学习之路(1)
  9. 蓝桥杯 ADV-193算法提高 盾神与条状项链
  10. 常见的文件格式有哪些
  11. java工程师项目简历_java软件工程师岗位项目经历怎么写
  12. 2个阶乘什么意思_两个阶乘号是什么意思,-双阶乘-数学-滕诓芳同学
  13. ectouch注册去邮箱的修改方法
  14. MIT 18.02 多变量微积分笔记总目录
  15. 解决阿里云oss 图片跨域问题
  16. CAD编辑指南7:新建空白图纸和新建表格、导入图片
  17. 达人评测 酷睿i7 1195g7和i7 1260p对比选哪个
  18. vue渐进式框架的理解
  19. 【测试】抓包技术哪家强?关于Burp、Fiddler、Charles三个工具的抓包测试
  20. 【51单片机】普中A2开发板 模块化编程 单片机入门 实例教学目录

热门文章

  1. HttpClient post 请求实例
  2. 编译原理 - 实验四 - yacc基本使用(bison)
  3. Java魔法堂:JVM的运行模式
  4. hdu 4323 Magic Number dp 多校联合赛(三)第四题
  5. ios开发之获取版本号,部分设备信息
  6. Mac的「预览」程序如何旋转图片
  7. 让MacOS废纸篓中超过30 天的文件自动清除
  8. Posterino常见问题:为什么渲染的文档在打印时看起来模糊?
  9. Mac电脑很早以前就删除的信息,现在想要恢复该怎么做?
  10. Mysql数据库一个表字段中存了id,并以逗号分隔,id对应的详细信息在另一个表中,实现查询的方法...