Tinker是Android上一套强大的补丁工具,它不仅支持dex的补丁,还支持资源和so的补丁,本文带大家来分析一下Tinker进行资源补丁的原理。
假设线上版本是1.0,当前开发完成的版本是2.0,我们要对1.0的版本下发补丁,使之升级到2.0。
1. 概览
使用Tinker完成一次补丁,要进行三个步骤:
生成差量补丁包(Diff)
补丁包也就是差量包,就是使用tinker-patch-cli工具,输入1.0和2.0的apk包,生成补丁包patch.zip。
合成全量资源包(Merge)
当客户端收到补丁包时,会在一个独立的进程,用补丁包与客户端的1.0的apk包进行合并,生成全量的新的资源包resource.apk。
加载全量资源包(Load)
在下一次启动app时,会通过反射注入的方式,改变LoadedApk的mResDir,使之指向resource.apk的目录,以及新创建一个包含resource.apk目录的AssetManager对象,设置到ResourcesManager中的Resources对象中。
2. 资源的定义
首先,我们需要知道一个APK包中哪些文件是资源。Diff的过程需要输入一个tinker_config.xml文件,其中定义了匹配资源文件名的正则表达式列表,如下所示:
因此,只要apk包中文件的名字匹配到这些正则表达式,那么就认为是资源,在生成和合成资源补丁的过程,就会被考虑到。
3. 生成差量补丁包
java -jar tinker-patch-cli.jar -old old.apk -new new.apk -config tinker_config.xml -out output_path
指定old.apk,new.apk,也就是本文中的1.0和2.0的apk,以及tinker_config.xml文件,和输出文件夹。
该命令的第一步是解析tinker_config.xml,得到新旧apk的文件目录、各种类型的补丁对应的pattern、输出文件夹的位置以及签名的文件等。
# CliMain.java
loadConfigFromXml(configFile, outputFile, oldApkFile, newApkFile); // 加载配置文件
tinkerPatch(); // 生成补丁包
生成补丁包具体的逻辑是在ApkDecoder,关键的代码是:
# ApkDecoder.java
public boolean patch(File oldFile, File newFile) throws Exception {
writeToLogFile(oldFile, newFile);
//check manifest change first
manifestDecoder.patch(oldFile, newFile);
// 将新旧apk分别解压到output_path/new和output_path/old目录
unzipApkFiles(oldFile, newFile);
// 遍历output_path/new目录中的每个文件,根据pattern使用对应的decoder进行patch
Files.walkFileTree(mNewApkDir.toPath(), new ApkFilesVisitor(config, mNewApkDir.toPath(), mOldApkDir.toPath(), dexPatchDecoder, soPatchDecoder, resPatchDecoder));
// get all du