分析命令行里面调用tinkerPatchRelease过程
1、 tinker-sample-android 中build.gradle
if (buildWithTinker()) {
apply plugin: 'com.tencent.tinker.patch'
2、tinker-patch-gradle-plugin 中 TinkerPatchPlugin.groovy
执行 TinkerPatchPlugin.apply(Project project)
TinkerPatchSchemaTask tinkerPatchBuildTask = mProject.tasks.create("tinkerPatch${variantName}", TinkerPatchSchemaTask)
tinkerPatchBuildTask.signConfig = variantData.variantConfiguration.signingConfig
variant.outputs.each { output ->
setPatchNewApkPath(configuration, output, variant, tinkerPatchBuildTask)
setPatchOutputFolder(configuration, output, variant, tinkerPatchBuildTask)
}
3、TinkerPatchSchemaTask.groovy
配置上一个apk路径、新的apk构建路径
builder.setOldApk(configuration.oldApk)
.setNewApk(buildApkPath)
.setOutBuilder(outputFolder)
.setIgnoreWarning(configuration.ignoreWarning)
.setAllowLoaderInAnyDex(configuration.allowLoaderInAnyDex)
.setRemoveLoaderForAllDex(configuration.removeLoaderForAllDex)
.setDexFilePattern(new ArrayList<String>(configuration.dex.pattern))
.setIsProtectedApp(configuration.buildConfig.isProtectedApp)
.setIsComponentHotplugSupported(configuration.buildConfig.supportHotplugComponent)
.setDexLoaderPattern(new ArrayList<String>(configuration.dex.loader))
.setDexIgnoreWarningLoaderPattern(new ArrayList<String>(configuration.dex.ignoreWarningLoader))
.setDexMode(configuration.dex.dexMode)
.setSoFilePattern(new ArrayList<String>(configuration.lib.pattern))
.setResourceFilePattern(new ArrayList<String>(configuration.res.pattern))
.setResourceIgnoreChangePattern(new ArrayList<String>(configuration.res.ignoreChange))
.setResourceIgnoreChangeWarningPattern(new ArrayList<String>(configuration.res.ignoreChangeWarning))
.setResourceLargeModSize(configuration.res.largeModSize)
.setUseApplyResource(configuration.buildConfig.usingResourceMapping)
.setConfigFields(new HashMap<String, String>(configuration.packageConfig.getFields()))
.setSevenZipPath(configuration.sevenZip.path)
.setUseSign(configuration.useSign)
InputParam inputParam = builder.create()
Runner.gradleRun(inputParam);
4、tinker-patch-lib Runner.java 来到了java的世界
protected void tinkerPatch() {
Logger.d("-----------------------Tinker patch begin-----------------------");
Logger.d(config.toString());
try {
//gen patch
ApkDecoder decoder = new ApkDecoder(config);
decoder.onAllPatchesStart();
decoder.patch(config.mOldApkFile, config.mNewApkFile);
decoder.onAllPatchesEnd();
//gen meta file and version file
PatchInfo info = new PatchInfo(config);
info.gen();
//build patch
PatchBuilder builder = new PatchBuilder(config);
builder.buildPatch();
} catch (Throwable e) {
e.printStackTrace();
goToError();
}
Logger.d("Tinker patch done, total time cost: %fs", diffTimeFromBegin());
Logger.d("Tinker patch done, you can go to file to find the output %s", config.mOutFolder);
Logger.d("-----------------------Tinker patch end-------------------------");
}
5、decoder.patch(config.mOldApkFile, config.mNewApkFile);
public boolean patch(File oldFile, File newFile) throws Exception {
writeToLogFile(oldFile, newFile);
//check manifest change first
manifestDecoder.patch(oldFile, newFile);
unzipApkFiles(oldFile, newFile);
Files.walkFileTree(mNewApkDir.toPath(), new ApkFilesVisitor(config, mNewApkDir.toPath(), mOldApkDir.toPath(), dexPatchDecoder, soPatchDecoder, resPatchDecoder));
// get all duplicate resource file
for (File duplicateRes : resDuplicateFiles) {
// resPatchDecoder.patch(duplicateRes, null);
Logger.e("Warning: res file %s is also match at dex or library pattern, "
+ "we treat it as unchanged in the new resource_out.zip", getRelativePathStringToOldFile(duplicateRes));
}
soPatchDecoder.onAllPatchesEnd();
dexPatchDecoder.onAllPatchesEnd();
manifestDecoder.onAllPatchesEnd();
resPatchDecoder.onAllPatchesEnd();
//clean resources
dexPatchDecoder.clean();
soPatchDecoder.clean();
resPatchDecoder.clean();
return true;
}
6、跟踪dex patch 来到 ApkFilesVisitor ApkDecoder.java
Files.walkFileTree(mNewApkDir.toPath(), new ApkFilesVisitor(config, mNewApkDir.toPath(), mOldApkDir.toPath(), dexPatchDecoder, soPatchDecoder, resPatchDecoder));
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Path relativePath = newApkPath.relativize(file);
Path oldPath = oldApkPath.resolve(relativePath);
File oldFile = null;
//is a new file?!
if (oldPath.toFile().exists()) {
oldFile = oldPath.toFile();
}
String patternKey = relativePath.toString().replace("\\", "/");
if (Utils.checkFileInPattern(config.mDexFilePattern, patternKey)) {
//also treat duplicate file as unchanged
if (Utils.checkFileInPattern(config.mResFilePattern, patternKey) && oldFile != null) {
resDuplicateFiles.add(oldFile);
}
try {
dexDecoder.patch(oldFile, file.toFile());
} catch (Exception e) {
throw new RuntimeException(e);
}
return FileVisitResult.CONTINUE;
}
if (Utils.checkFileInPattern(config.mSoFilePattern, patternKey)) {
//also treat duplicate file as unchanged
if (Utils.checkFileInPattern(config.mResFilePattern, patternKey) && oldFile != null) {
resDuplicateFiles.add(oldFile);
}
try {
soDecoder.patch(oldFile, file.toFile());
} catch (Exception e) {
throw new RuntimeException(e);
}
return FileVisitResult.CONTINUE;
}
7、分别进行 soDecoder.patch(oldFile, file.toFile()); dexDecoder.patch(oldFile, file.toFile()); resDecoder.patch(oldFile, file.toFile());
public ApkDecoder(Configuration config) throws IOException {
super(config);
this.mNewApkDir = config.mTempUnzipNewDir;
this.mOldApkDir = config.mTempUnzipOldDir;
this.manifestDecoder = new ManifestDecoder(config);
//put meta files in assets
String prePath = TypedValue.FILE_ASSETS + File.separator;
dexPatchDecoder = new UniqueDexDiffDecoder(config, prePath + TypedValue.DEX_META_FILE, TypedValue.DEX_LOG_FILE);
soPatchDecoder = new BsDiffDecoder(config, prePath + TypedValue.SO_META_FILE, TypedValue.SO_LOG_FILE);
resPatchDecoder = new ResDiffDecoder(config, prePath + TypedValue.RES_META_TXT, TypedValue.RES_LOG_FILE);
resDuplicateFiles = new ArrayList<>();
}