Tinker使用及源码分析

tinker 再学习

2020-06-29  本文已影响0人  David_zhou
学习是一个不断反思改进的过程,对于tinker这种矿,可以开采多次。现在发现一些问题和一个改进的思路,       
待改进点

之前看tinker的时候,重点在看流程,包括打patch以及合成加载的patch,但后续发现知道流程帮助并不大。而之前忽略了tinker涉及到的一些知识点,比如混合编译,内联处理。在看的过程中,自己也是以旁观者的身份来看代码,导致看的很浅。tinker文章说到的一些坑,并没有体会到,只是知道了是这样,但为什么这样并没有去思考。就像走在一条已经修好的路上,修路的人说这条路有多少坑,但是走路的人很难注意到,于是对tinker涉及到的知识点都没有注意到。

新思路

后面和人在探讨时,发现一种思路:看代码时站在开发者的角度,去想如果自己去写,会怎样写。对于特殊的地方要去想,为什么要这样设计。这样就能对比代码的一些思路,也能更好的提升自己。

问题

学习的时候又发现有很多知识点并没有学到,所以带着问题去看代码也是比较好的。
暂时发现的问题有如下:

tinker的内联处理
[ART下的方法内联策略及其对Android热修复方案的影响分析] 见参考文献二
Tinker最终采用的应对方案是去掉ART环境下的合成增量Dex的逻辑,直接合成全量的NewDex,
这样除了loader类,所有方法统一都用了NewDex里的,也就不怕有方法被内联了。
至于全量新Dex在系统OTA之后触发dex2oat可能导致App启动时ANR的问题,
Tinker是通过在进入ApplicationLike之前判断fingerprint是否变化来得知
系统是否进行过OTA,然后根据判断结果手动触发多线程dex2oat加以缓解的。
为什么使用application代理
一个可能的原因是可以修复applicationlike中的类
另一个可能的原因是 我们的Application类是一定会通过PathClassloader加载的,
所以我们需要将Application类与我们的逻辑解耦。
另外如果使用instant run的方式替换application,会存在潜在的兼容问题,而且不可逆。
tinker的资源加载 (和webview的资源加载相冲突如何处理)
  资源合成    
在UpgradePatch中的tryPatch中会去调用ResDiffPatchInternal的tryRecoverResourceFiles方法,
->patchLibraryExtractViaBsDiff()->()最终调用BSPatch.patchFast()完成资源的合成。
  如何替换
 Tinker的资源更新采用的InstantRun的资源补丁方式,全量替换资源。由于App加载资源是
依赖Context.getResources()方法返回的Resources对象,Resources 内部包装AssetManager.
最终由 AssetManager 从 apk 文件中加载资源。我们要做的就是新建一个AssetManager(),
hook掉其中的addAssetPath()方法,将我们的资源补丁目录传递进去,
然后循环替换Resources对象中的AssetManager对象,达到资源替换的目的。

简单描述为反射调用新建的AssetManager的addAssetPath将路径穿进去,然后主动调ensureStringBlocks
方法确保资源的字符串索引创建出来;然后循环遍历持有Resources对象的references集合,
依次替换其中的AssetManager为新建的AssetManager,最后调Resources.updateConfiguration
将Resources对象的配置信息更新到最新状态,
完成整个资源替换的过程。
参考文献四
混合编译
混合编译运行主要指AOT编译,解释执行与JIT编译,它主要解决的问题有以下几个:
应用安装时间过长;降低占ROM空间;提升系统与应用性能;快速的系统升级
Android N为了解决这些问题,通过管理解释,AOT与JIT三种模式,以达到一种运行效率、
内存与耗电的折中。简单来说,在应用运行时分析运行过的代码以及“热代码”,
并将配置存储下来。在设备空闲与充电时,ART仅仅编译这份配置中的“热代码”。

tinker使用的方案是参考文献三中的运行时替换PathClassLoader方案
事实上,App image中的class是插入到PathClassloader中的ClassTable中。假设我们完全废弃
掉PathClassloader,而采用一个新建Classloader来加载后续的所有类,即可达到将cache无用化的效果。
参考文献三
安全模式如何生效,变量存在内存中,如何影响下一次启动
 变量不是存放在内存中,是存放在sp中,下一次启动直接读取.
如何利用tinker更新so,lib等
 如果只是更新so文件,可以在真正的application中设置tinkerFlags参数,
可以设置的选项包括只支持更改dex,只更改resource,只更改so,默认所有的都可以更新。
可以在tinker.gradle中设置dex,lib,resource的pattern.
 但如果是针对好几个版本进行so热更新,那么如何处理tinkerId不一样的问题?
笨办法-》每个版本进行单独的patch.更好的办法是?
如何关闭开发模式下的tinker
使用反射Application方式接入:可以直接在build.gradle中将
apply from: 'tinker-support.gradle'注释掉。
改造Application方式接入:先将tinkerSupport中overrideTinkerPatchConfiguration
设置为false 修改成将tinkerSupport中enable设置为false。 

判断dex的patch是否成功

补丁中的test.dex文件中只包含了TinkerTestDexLoad 这个类,而且属性值是true,
这个dex不是编译生成的,而是作为文件直接拷贝到patch中的。如果没有加载补丁,
就直接读取TinkerTestDexLoad中的isPatch属性,如果加载成功,
就会优先加载patch中的dex,所以TinkerTestDexLoad这个类
就是从patch中的test.dex中加载而来。此时isPath属性为true.
所以去读取TinkerTestDexLoad的isPatch属性是合理且可行的。
res的检查类似,检查方法在checkResUpdate()
clean patch补救措施的原理
当检测到dex或者res patch失败时,会将dex数组从中间重新拷贝,然后反射替换。
加固
加固模式的处理
classverify
tinkerId的作用
混淆的处理
每次启动都进行patch
如何处理新增的四大组件
so的热更新
oat文件
dex数据格式
TinkerApplicationInlineFence类的作用
那些类会被打包进maindex,分包规则如何定义
为何要加入dex的loader参数?
为什么有些补丁包有changed_classes.dex 有些是classes.dex+classes2.dex这样的
如何判断手机进行了OTA

感谢tinker团队的无私开源和先行者的分享。
Tinker:技术的初心与坚持
ART下的方法内联策略及其对Android热修复方案的影响分析
Android N混合编译与对热补丁影响解析
Android热补丁之Tinker原理解析

上一篇下一篇

猜你喜欢

热点阅读