Dev Club 是一个交流移动开发技术,结交朋友,扩展人脉的社群,成员都是经过审核的移动开发工程师。每周都会举行嘉宾分享,话题讨论等活动。 本期,我们邀请了腾讯WXG Android开发工程师——张绍文,为大家分享《微信热补丁Tinker的实践演进之路》。 如何加入 Dev Club?
分享内容简介: Tinker 是微信官方的 Android 热补丁解决方案,它支持动态下发代码、So库以及资源,让应用能够在不需要重新安装的情况下实现更新。这里大致介绍 Tinker 的实现原理,当时遇到的各种坑以及对它各个方面性能的优化工作。 内容大体框架:
分享人介绍: 张绍文 微信 目前负责开发微信Android与网络组件优化工作 下面是本期分享内容整理 hello,大家好。我是张绍文,目前在微信主要负责 Android 的性能优化以及终端质量平台相关工作。 下面开始我们今天的分享。 1. 当前各种热补丁框架对比 & Tinker 的设计目标热补丁技术是当前非常热门的 Android 开发技术,其中比较出名的方案有支付宝的 AndFix以及** QZone 的超级热补丁** 方案。 微信大约在2015年6月开始尝试应用,经过研究与尝试现有的各个方案,我们发现它们都有着自身的一些局限性。我们最终采用不同于它们的技术方案,自研微信热补丁开源框架 Tinker。 下面我们先来讲讲先有框架的一些局限性。 1.1 AndFixAndfix 是阿里推出的开源框架,它在 github 的地址是: 它的技术原理如下图:它采用 native hook 的方式,这套方案直接使用 它的缺点主要包括以下几个:
Andfix 的好处是可以立刻生效,但它可以支持的补丁场景非常有限,仅仅可以使用它来修复特定问题。 所以我们不考虑采用这个方案。 1.2 Qzone 超级补丁方案现在我们讲讲 Qzone 超级补丁方案,在腾讯内部已开源。 这个方案使用 classloader 的方式,能实现更加友好的类替换。而且这与我们加载 Multidex 的做法相似,能基本保证稳定性与兼容性。 它主要的面临问题有两个:
Qzone 的方案最为简单,而且开发透明,补丁的成功率也是非常高的。 但由于微信对于运行性能以及补丁大小 都比较敏感,我们最终也没有采用这套方案。 1.3 Tinker 的设计目标那么微信希望的是一套怎么样的热补丁框架呢,我们认为主要的目标有以下几个:
2. 3. Tinker 的实践演进现在我们来讲讲微信热补丁框架 Tinker 的实现,目前在腾讯内部已开源。 它的名字来至 Dota 中的地精修补匠,我们希望发版本可以像它一样做到无限刷新。 Tinker 的方案来源 gradle 编译的 instant run 与 buck 编译的 exopackage。它们的思想都是全量替换新的 Dex。即我们完全使用了新的 Dex,那样既不出现 Art 地址错乱的问题,在 Dalvik 也无须插桩。 但是 instant run 针对的是编译期,它可以直接将最后生成的所有变化都直接拷到手机端。对于线上方案,这肯定是不可行的。所以当前核心问题是找到合适的,使补丁结果更小的差分算法。 微信首先 demo 中采用的是 bsdiff,它无关文件格式,但对于dex效果不是特别好,而且非常不稳定。当前微信对于 so,依然使用 bsdiff 算法。 然后我们想到 dexmerge 算法,把修改跟新增的类通过 dexmerge 方式与原来的 dex 合并,从而得到最终的完整 Dex。 经过实践,dexmerge 的核心问题有两个:
最后我们决定基于 dex 的格式,自研出一种 Dexdiff 算法,它需要达到以下目标;
这里面主要的原理是深度利用原来 dex 中的信息,对于 dex 的每一个 section 做处理。这块在今天不再深入,感兴趣的同学可以交流。 内存方面 dexdiff 峰值内存是 dex 的两倍左右,达到预期的结果。 对于微信热补丁的更多信息,可以阅读我之前发的一篇文章。 然后我们来看看 Tinker 的框架设计,它主要包括以下几部分:
在微信中,我们为 Tinker 框架加入了100多个实时上报,监控着在每个过程可能出现的问题: 3.Tinker 在实现中遇到的困难接下来我们来看看在开发 Tinker 过程中,遇到的一些问题: 1. 厂商 OTA;对于 Art 平台,dex2oat 时间较长。特别是厂商 OTA 之后,所有动态加载的代码都需要重新执行 dex2oat。这是因为 boot image 已经改变,但是系统在升级时只会给 ClassN.dex 重新 OTA。 对于补丁 dex 会出现主进程同步执行 dex2oat,这个时间非常久,很有可能会出现 ANR,对于小米等一些产品的开发板更是如此。这也是我们现在努力在实现分平台合成的原因,即在 Art平台,只合成规则下需要的 class。只要不是全量替换,重新 dex2oat 的时间是可以接受的。 2. Android N 混合编译导致补丁机制失效这块花了一定的时间重新梳理了 Android N art 的代码,详细的分析可以查看之前我发的一篇文章。 3. Dex 反射成功但是不生效;开始的时候,我们加载补丁 dex 采用的是 makedexElement 的方式。但是发现大约有几十万台机器,补丁加载成功了,但是使用的还是旧版本的代码。某些机器类似三星 s6 502系统,尽管反射 pathList 成功,查找顺序依然以 base.apk 优先。 这里采取的解决方法是类似 instant run,采用反射 parent classloader 的方式。这里不得不提,instant run 的 increaseClassLoader 实现非常精妙。 4. Xposed 等微信插件;市面上有各种各样的微信插件,它们在微信启动前会提前加载微信中的类,这会导致两个问题: a. 在 Dalvik 平台,直接出现 b. 在 Art 平台,由于出现部分类使用了旧的代码,这可能导致补丁无效,或者地址错乱的问题。 它们根本的原因都是Xposed反射调用,提前导入了我们的某些类。 事实上,由于补丁使用不当或者其他问题,我们的确需要有一个安全模式。即在应用启动不起来或多次 crash 时,进入补丁清理或者升级的流程。 结语也许有人觉得 Tinker 过于臃肿,过于复杂。这是因为热补丁并不是仅仅加载一个 dex 或 so 文件,事实上它要关心的细节有很多。进程的一致性,控制可修改类的范围,版本的管理,扩展性等等。 Tinker 的未来规划是真正的开源出去,大约下周会提交分享平台合成以及资源相关的所有代码。然后等公司的开源审计结束后将在 github 开源,欢迎大家接入 Tinker 内测,给我们更多的意见。 由于时间有限,今天的分享就到这里。对于 So,资源的合成方式,dexdiff 的技术细节,若大家感兴趣可以与我们交流。 互动问答环节Q1: 请教下 patch 进程和主进程是怎么通信的?
Q2:“分平台合成”没听太明白,能再仔细说下么?
Q3: 对于内部空间不足引起的 patch 失败现在有什么好的解决办法?
Q4: 对于替换 classloader 失败后再用 MultiDex install 这种方案有什么考虑?
Q5: 目前微信对热补丁技术的应用场景一般集中在哪些方面呢?除了修复紧急的 BUG,还有哪些真实场景下用过这个技术吗?微信是如何评估是否需要通过打热补丁的方式来处理一些问题的呢?
Q6: 想问下大神,对于替换 app 中使用的第三方 jar 包,有具体实践吗?
Q7: patchCoreSDK 怎么绕过 换 classloader 后跨 dex 加载类 accesserror 的问题?有对 patchcoreSDK 做强制访问隔离吗?
Q8: patch 成功后怎么及时重启其他进程?
Q9: 完全使用新的资源包是怎么理解?旧的资源包会被替换删除吗?
Q10: 超级补丁方案,有没有想过不采用插桩的方式,而是去 hook 检验的方法,就能缓解性能的问题?
Q11: 合成新的资源和 so 是怎么加载的?
Q12: 是否有动态下发第三方的 jar 包,如何调用第三方 jar 包的方法。反射?
Q13: 差量下发更新,合成的时候是否会有性能问题?是否支持(图片)资源的差量下发?
Q14: 需要在补丁合成加载之后才进入程序(交由用户操作)吗?
Q15: 代码完全开源吗?
Q16: xposed 框架的那些插件,是通过反射调用替换值?那一般有啥方式保证安全性?保证 app 数据的安全性?
Q17: 为什么要在补丁成功的时候加结果回调是为了启动程序么,但是和您刚才说的为了实时上报?
Q18: 既然能加载 so 和资源,Tinker 能用于插件化吗?
Q19: merge 失败后的补救机制是怎样的?可以回退么?
Q20: 这套框架目前是多少个人在维护呢?
Q21: 请问资源是编译到 arsc 中还是反射加载二进制流?
Q22: 在加入 Tinker 之后,对各平台的加固适配如何?微信是否有加固?
|
|