本系列 Tinker 源码解析基于 Tinker v1.9.12
加载资源补丁流程
将到资源补丁的加载,首先还要回过头来先看资源补丁的校验和检查。
我们回到 TinkerLoader.tryLoadPatchFilesInternal 方法中来看。
tryLoadPatchFilesInternal
1 | //check resource |
具体的校验是在 TinkerResourceLoader.checkComplete 中完成的。这里为了校验的速度,所以只会校验资源补丁存不存在。
checkComplete
checkComplete 方法我们分段来看吧
1 | // 读取 assets/res_meta.txt |
为了校验的速度,只读取了 assets/res_meta.txt 的第一行,并存入到 resPatchInfo 中
res_meta.txt 的第一行主要是资源的 crc 值和 md5 值 ,在后面会做校验。
1 | if (resPatchInfo.resArscMd5 == null) { |
校验上面读取到的 md5 值是否为空以及 md5 值长度是否是 32 位
1 | String resourcePath = directory + "/" + RESOURCE_PATH + "/"; |
校验资源补丁文件夹是否存在。
1 | File resourceFile = new File(resourcePath + RESOURCE_FILE); |
校验资源补丁文件是否存在及合法性。
1 | try { |
通过 context 来检查当前环境是否支持加载资源补丁。方法里面做的事就是通过反射来获取各种系统的属性和方法。简单地举例以下几种:
- ActivityThread : 当前的 ActivityThread 实例,app主线程的入口。利用 ActivityThread 可以获取到 LoadedApk 对象;
- LoadedApk : 通过 LoadedApk 可以获取 mResDir 属性;
- mResDir : 这个值很关键,就是资源文件的路径。在后面会被 hook 成资源补丁的路径;
- addAssetPath : 通过 addAssetPath 方法将资源补丁文件加载进新的 AssetManager 中;
- mActiveResources : ResourcesManager 的 Resources 容器。里面会存储着每个 apk 对应的 Resources 对象。mActiveResources 是 ArrayMap 类型的,不同的 apk 都有一个不同的 key 来获取对应的 apk 的 Resource 对象;
- mAssets : 即 Resources 类中的 mAssets 属性,其实就是一个 AssetManager 对象。在资源打补丁的时候,Resources 中原来的 mAssets 对象会被替换成新的 AssetManager 对象。
这里就不详细讲了,总结起来就一句话:获取 Android 系统中与资源有关的一些属性和方法,为接下来的加载资源补丁做准备。如果在 isResourceCanPatch 方法中报出异常了,就认为当前环境不能加载资源补丁了。
tryLoadPatchFilesInternal
然后我们再在 tryLoadPatchFilesInternal 中往下看。会看到资源补丁加载代码的入口,即 TinkerResourceLoader.loadTinkerResources 方法
1 | //now we can load patch resource |
loadTinkerResources
loadTinkerResources 方法我们分段来看。
1 | // 检查 res_meta.txt 中读取出来的 md5 值,如果 resPatchInfo 或者 md5 是空的,就说明补丁包中没有资源补丁,不需要加载 |
然后就是加载资源补丁了,如果加载失败了,会把 dex 补丁卸载了。防止 dex 补丁代码中会引用到资源补丁中的资源文件,导致程序崩溃或报错。
1 | try { |
monkeyPatchExistingResources
monkeyPatchExistingResources 方法也一段一段来看
1 | // 检查资源补丁apk是否为空 |
上面这段代码基本上都有注释了,接着往下看
1 | // Create a new AssetManager instance and point it to the resources installed under |
在创建出新的 AssetManager 之后,最后要做的事就是用新的 AssetManager 来替换旧的。下面代码中的 references 就是上面提到的 mActiveResources 的 value 集合。也就是每个 apk 的 Resources 资源集合。
1 | for (WeakReference<Resources> wr : references) { |
最后,就是来确认一下资源补丁是否已经加载成功了。具体的方法就是在资源补丁Apk的 assets 中有一个 Tinker 的测试资源,名字叫 only_use_to_test_tinker_resource.txt ,如果可以正确读取到并且没报错的话,就证明资源补丁加载成功了。否则就抛出异常,会执行 dex 补丁卸载的流程。
1 | if (!checkResUpdate(context)) { |
到这里,资源补丁的加载流程就讲完了。