设为首页 加入收藏

TOP

【腾讯Bugly干货分享】微信热补丁Tinker的实践演进之路(二)
2017-10-13 10:47:26 】 浏览:2683
Tags:腾讯 Bugly 干货 分享 补丁 Tinker 实践 演进
nker 框架加入了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 的代码,详细的分析可以查看之前我发的一篇文章。

Android N混合编译与对热补丁影响解析

3. Dex 反射成功但是不生效;

开始的时候,我们加载补丁 dex 采用的是 makedexElement 的方式。但是发现大约有几十万台机器,补丁加载成功了,但是使用的还是旧版本的代码。某些机器类似三星 s6 502系统,尽管反射 pathList 成功,查找顺序依然以 base.apk 优先。

这里采取的解决方法是类似 instant run,采用反射 parent classloader 的方式。这里不得不提,instant run 的 increaseClassLoader 实现非常精妙。

4. Xposed 等微信插件;

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

a. 在 Dalvik 平台,直接出现 Class ref in pre-verified class resolved to unexpected implementation 的 crash;

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

它们根本的原因都是Xposed反射调用,提前导入了我们的某些类。

事实上,由于补丁使用不当或者其他问题,我们的确需要有一个安全模式。即在应用启动不起来或多次 crash 时,进入补丁清理或者升级的流程。

结语

也许有人觉得 Tinker 过于臃肿,过于复杂。这是因为热补丁并不是仅仅加载一个 dex 或 so 文件,事实上它要关心的细节有很多。进程的一致性,控制可修改类的范围,版本的管理,扩展性等等。

Tinker 的未来规划是真正的开源出去,大约下周会提交分享平台合成以及资源相关的所有代码。然后等公司的开源审计结束后将在 github 开源,欢迎大家接入 Tinker 内测,给我们更多的意见。

由于时间有限,今天的分享就到这里。对于 So,资源的合成方式,dexdiff 的技术细节,若大家感兴趣可以与我们交流。

互动问答环节

Q1:请教下 patch 进程和主进程是怎么通信的?

是通过 intent service 通信的,主进程一个接受补丁结果的 intent service,patch 进程是一个接受补丁请求的 intent service

Q2:“分平台合成”没听太明白,能再仔细说下么?

分平台合成就是在 Dalvik 平台,我们合成全量的 dex,这可以避免我们插桩的要求。

在 Art 平台,我们只合成上述三个条件下的类:

a. 修改跟新增的 class;
b. 若 class 有 field,method 或 interface 数量变化,它们所有的子类;
c. 若 class 有 field,method 或 interface 数量变化,它们以及它们所有子类的调用类。如果采用 ClassN 方式,即需要多个 dex 一起处理。

这里的难点是同一份 diff 代码,可以做到不同的合成方式。

Q3:对于内部空间不足引起的 patch 失败现在有什么好的解决办法?

对于我们的方案,空间占用有可能比较大,我们解决的方法有两个:

  1. 在 patch 之前提前检查用户的剩余空间,如果用户剩余空间过少,即不尝试。
  2. 若本次失败,我们会有回调,然后我们会定期重试三次。

你也可以在这里采用提示用户清理空间。Tinker 框架是可以高度定制化的。

Q4:对于替换 classloader 失败后再用 MultiDex install 这种方案有什么考虑?

有的,对于替换失败的话,的确会回退到类似 Multidex install 方式的

Q5:目前微信对热补丁技术的应用场景一般集中在哪些方面呢?除了修复紧急的 BUG,还有哪些真实场景下用过这个技术吗?微信是如何评估是否需要通过打热补丁的方式来处理一些问题的呢?

正如我之前的一篇文章来说,在 Android 热补丁技术的应用比 iOS 更加容易。我们可以完全做到无感知的开发,推给用户等。这里面的应用场景有很多,用户调试,版本升级,发布需求,Abtest 等等。

Q6:想问下大神,对于替换 app 中使用的第三方 jar 包,有具体实践吗?

抱歉,这部分还没有实践。原理上是没问题的,如果第三方的 jar 包是集成到源码,那么编译新包的时候已经可以带上改变。如果第三方的 jar 包是动态加载的,也是没有问题的。我们通过 parent classloader 的方式,查找顺序也会在你们之前。

Q7:patchCoreSDK 怎么绕过 换 classloader 后跨 dex 加载类 accesserror 的问题?有对 patchcoreSDK 做强制访问隔离吗?

是的,Tinker 框架分为两部分,核心加载代码,成为 loader 类,这里大概有十几个类,他们是不允许修改的。其他大部分 Tinker 的类也是可以通过补丁修改的,这里 Tinker 框架已经做了处理,即在新合成的 Dex,我们已经删除了 loader 相关的类,从而彻底避免了这个问题。

Q8:patch 成功后怎么及时重启其他进程?

为了保证各个进程的唯一性,我们有一个版本管理文件用于记录当前补丁的版本。它分为 old 与 new 两个字段。同时做了约定,只有 patch 进程可以修改 new 字段,只有主进程可以修改 old 字段,其他所有进程启动时都只会加载 old 字段的补丁版本。然后主要主进程可以发起版本升级,即把 new 字段赋值给 old 字段,这个时候主进程要杀掉其他所有的进程,以保证统一性。
而及时重启其他进程的问题,主要是在我刚才讲的 result service。在结果回调中,我们如果发现补丁已经成功了,我们可以设置主进程在后台或者锁屏时自杀,以达到最快的应用。

Q9:完全使用新的资源包是怎么理解?旧的资源包会被替换删除吗?

旧的资源包是安装的 apk,我们是不会删掉的。我们只是反射系统的一些接口,把它替换成新的资源包

Q10:超级补丁方案,有没有想过不采用插桩的方式,而是去 hook 检验的方法,就能缓解性能的问题?

事实上,有些人实现 hook preverify 标志来避免插桩。但是看过底层代码,就知道是不可行的。我们要知道系统检查那个标志位的真正原因,即使 hook 了 preverify 标志,在真正运行过程中,由于 quck 指令以及 vtable 的优化,依然运行时会出问题。这个问题告诉我们,做事情需要知其然也要知其所以然。

Q11:合成新的资源和 so 是怎么加载的?

so 可以通过反射 classloader 的 lib path,但是我们并不建议这么多,一来是兼容性问题,二来在

首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇结合Retrofit,RxJava,Okhttp,Fast.. 下一篇Android与单片机通信常用数据转换..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目