本系列 Tinker 源码解析基于 Tinker v1.9.12
校验so补丁流程
与加载资源补丁类似,加载so补丁也要先从校验开始看起。
其实总体来说,Tinker 中加载 so 补丁文件的关键代码就一句:
System.load(String filePath)
tryLoadPatchFilesInternal
1 | final boolean isEnabledForNativeLib = ShareTinkerInternals.isTinkerEnabledForNativeLib(tinkerFlag); |
checkComplete
从 assets/so_meta.txt 中读取 so 补丁信息,每一条 so 补丁信息都会被封装成一个 ShareBsDiffPatchInfo 对象,然后放入 libraryList 中。
1 | String meta = securityCheck.getMetaContentMap().get(SO_MEAT_FILE); |
然后遍历 libraryList ,去校验里面的 ShareBsDiffPatchInfo 对象中 md5 和 name 值是否合法。合法的 ShareBsDiffPatchInfo 对象再放入 libs 中。
1 | //tinker//patch-641e634c/lib |
接着会校验 so 补丁文件夹是否存在
1 | File libraryDir = new File(libraryPath); |
再校验上面的从 so_meta.txt 中获取到的 so 补丁文件路径是否真的存在并且 so 文件是可读的
1 | //fast check whether there is any dex files missing |
都没问题的话,就通过校验
1 | //if is ok, add to result intent |
加载so补丁流程
加载so补丁的入口:TinkerLoadLibrary.loadArmLibrary / TinkerLoadLibrary.loadArmV7Library ,区别就是前者用来加载 armeabi 平台的,后者是用来加载 armeabi-v7a 平台的。我们就来看 loadArmLibrary 方法吧。
loadArmLibrary
1 | public static void loadArmLibrary(Context context, String libName) { |
loadLibraryFromTinker
loadLibraryFromTinker 一开始校验了 libName 的名字是否是 lib 开头、 .so 结尾的。
1 | final Tinker tinker = Tinker.with(context); |
到这里,Tinker 中关于 so 补丁加载的流程就讲完了。
番外
大家有没有发现,一个个单独去调用 TinkerLoadLibrary.loadArmLibrary 会很麻烦,因为如果我的 so 补丁文件有很多个,就需要调用很多次。所以从 Tinker v1.7.7 之后,提供了一键反射的方案来加载 so 补丁文件。
具体方法 TinkerLoadLibrary.installNavitveLibraryABI
installNavitveLibraryABI
来看一下具体的代码:
1 | public static boolean installNavitveLibraryABI(Context context, String currentABI) { |
在做了一堆的检查之后,具体 so 文件加载是在 installNativeLibraryPath 方法中。
installNativeLibraryPath
installNativeLibraryPath 中做的事情主要有两点:
- 如果 classloader 中没有注入 so 补丁文件夹的路径的话,就执行注入;
- 如果 classloader 中已经有 so 补丁文件夹的路径了,就先删除,再进行注入;
具体 hook 的代码根据 SDK 版本而定,这里就不展开讲了。
1 | private static void installNativeLibraryPath(ClassLoader classLoader, File folder) |
综上所述,有了 TinkerLoadLibrary.installNavitveLibraryABI 之后,你就只需要传入当前手机系统的 ABI ,就无需再对 so 的加载做任何的介入。