源码分析入口-TinkerInstaller.install
分析源码最好从调用的入口进行入手分析,因此我们从TinkerInstaller.install入手:
//TinkerInstaller.install(mApplicationLike);
TinkerInstaller.install(
mApplicationLike,
new DefaultLoadReporter(getApplication()),
new DefaultPatchReporter(getApplication()),
sCustomPatchListener,
CustomResultService.class,
new UpgradePatch()
);
TinkerInstaller的设计应用到了外观模式,相关的核心逻辑方法都由Tinker类来完成:
public class TinkerInstaller {
private static final String TAG = "Tinker.TinkerInstaller";
public static Tinker install(ApplicationLike applicationLike) {
Tinker tinker = new Tinker.Builder(applicationLike.getApplication()).build();
Tinker.create(tinker);
tinker.install(applicationLike.getTinkerResultIntent());
return tinker;
}
public static Tinker install(ApplicationLike applicationLike, LoadReporter loadReporter, PatchReporter patchReporter,
PatchListener listener, Class<? extends AbstractResultService> resultServiceClass,
AbstractPatch upgradePatchProcessor) {
Tinker tinker = new Tinker.Builder(applicationLike.getApplication())
.tinkerFlags(applicationLike.getTinkerFlags())
.loadReport(loadReporter)
.listener(listener)
.patchReporter(patchReporter)
.tinkerLoadVerifyFlag(applicationLike.getTinkerLoadVerifyFlag()).build();
Tinker.create(tinker);
tinker.install(applicationLike.getTinkerResultIntent(), resultServiceClass, upgradePatchProcessor);
return tinker;
}
public static void cleanPatch(Context context) {
Tinker.with(context).cleanPatch();
}
public static void onReceiveUpgradePatch(Context context, String patchLocation) {
Tinker.with(context).getPatchListener().onPatchReceived(patchLocation);
}
public static void setLogIml(TinkerLog.TinkerLogImp imp) {
TinkerLog.setTinkerLogImp(imp);
}
}
因此,我们的关注点转移到Tinker类中,下面是省略后的Tinker代码,可以明确得看到:Tinker是一个单利,这里也用到了Builder模式来构建Tinker的单利:
public class Tinker {
private static final String TAG = "Tinker.Tinker";
private static Tinker sInstance;
private static boolean sInstalled = false;
final Context context;
final File patchDirectory;
final PatchListener listener;
final LoadReporter loadReporter;
final PatchReporter patchReporter;
final File patchInfoFile;
final File patchInfoLockFile;
final boolean isMainProcess;
final boolean isPatchProcess;
final boolean tinkerLoadVerifyFlag;
int tinkerFlags;
TinkerLoadResult tinkerLoadResult;
private boolean loaded = false;
private Tinker(Context context, int tinkerFlags, LoadReporter loadReporter, PatchReporter patchReporter,
PatchListener listener, File patchDirectory, File patchInfoFile, File patchInfoLockFile,
boolean isInMainProc, boolean isPatchProcess, boolean tinkerLoadVerifyFlag) {
this.context = context;
this.listener = listener;
this.loadReporter = loadReporter;
this.patchReporter = patchReporter;
this.tinkerFlags = tinkerFlags;
this.patchDirectory = patchDirectory;
this.patchInfoFile = patchInfoFile;
this.patchInfoLockFile = patchInfoLockFile;
this.isMainProcess = isInMainProc;
this.tinkerLoadVerifyFlag = tinkerLoadVerifyFlag;
this.isPatchProcess = isPatchProcess;
}
public static Tinker with(Context context) {
if (!sInstalled) {
throw new TinkerRuntimeException("you must install tinker before get tinker sInstance");
}
if (sInstance == null) {
synchronized (Tinker.class) {
if (sInstance == null) {
sInstance = new Builder(context).build();
}
}
}
return sInstance;
}
public static void create(Tinker tinker) {
if (sInstance != null) {
throw new TinkerRuntimeException("Tinker instance is already set.");
}
sInstance = tinker;
}
public void install(Intent intentResult, Class<? extends AbstractResultService> serviceClass,
AbstractPatch upgradePatch) {
sInstalled = true;
TinkerPatchService.setPatchProcessor(upgradePatch, serviceClass);
TinkerLog.i(TAG, "try to install tinker, isEnable: %b, version: %s", isTinkerEnabled(), ShareConstants.TINKER_VERSION);
if (!isTinkerEnabled()) {
TinkerLog.e(TAG, "tinker is disabled");
return;
}
if (intentResult == null) {
throw new TinkerRuntimeException("intentResult must not be null.");
}
tinkerLoadResult = new TinkerLoadResult();
tinkerLoadResult.parseTinkerResult(getContext(), intentResult);
//after load code set
loadReporter.onLoadResult(patchDirectory, tinkerLoadResult.loadCode, tinkerLoadResult.costTime);
if (!loaded) {
TinkerLog.w(TAG, "tinker load fail!");
}
}
public static class Builder {
private final Context context;
private final boolean mainProcess;
private final boolean patchProcess;
private int status = -1;
private LoadReporter loadReporter;
private PatchReporter patchReporter;
private PatchListener listener;
private File patchDirectory;
private File patchInfoFile;
private File patchInfoLockFile;
private Boolean tinkerLoadVerifyFlag;
public Builder(Context context) {
if (context == null) {
throw new TinkerRuntimeException("Context must not be null.");
}
this.context = context;
this.mainProcess = TinkerServiceInternals.isInMainProcess(context);
this.patchProcess = TinkerServiceInternals.isInTinkerPatchServiceProcess(context);
this.patchDirectory = SharePatchFileUtil.getPatchDirectory(context);
if (this.patchDirectory == null) {
TinkerLog.e(TAG, "patchDirectory is null!");
return;
}
this.patchInfoFile = SharePatchFileUtil.getPatchInfoFile(patchDirectory.getAbsolutePath());
this.patchInfoLockFile = SharePatchFileUtil.getPatchInfoLockFile(patchDirectory.getAbsolutePath());
TinkerLog.w(TAG, "tinker patch directory: %s", patchDirectory);
}
//省略一部分代码
}
}
```
很简单地,通过with方法就可以获取一个Tinker的实例。
###源码分析入口-TinkerInstaller.onReceiveUpgradePatch
另外一个入口就是我们加载Patch的时候调用到了TinkerInstaller.onReceiveUpgradePatch:
```java
TinkerInstaller.onReceiveUpgradePatch(getApplication(), path);
```
也就是调用:
```java
public static void onReceiveUpgradePatch(Context context, String patchLocation) {
Tinker.with(context).getPatchListener().onPatchReceived(patchLocation);
}
```
这里先获取了Tinker实例,然后获取PatchListener,最终调用PatchListener的onPatchReceived方法。其中PatchListener是一个接口,默认的实现是DefaultPatchListener:
```java
public class DefaultPatchListener implements PatchListener {
protected final Context context;
public DefaultPatchListener(Context context) {
this.context = context;
}
@Override
public int onPatchReceived(String path) {
File patchFile = new File(path);
int returnCode = patchCheck(path, SharePatchFileUtil.getMD5(patchFile));
if (returnCode == ShareConstants.ERROR_PATCH_OK) {
TinkerPatchService.runPatchService(context, path);
} else {
Tinker.with(context).getLoadReporter().onLoadPatchListenerReceiveFail(new File(path), returnCode);
}
return returnCode;
}
protected int patchCheck(String path, String patchMd5) {
Tinker manager = Tinker.with(context);
//check SharePreferences also
if (!manager.isTinkerEnabled() || !ShareTinkerInternals.isTinkerEnableWithSharedPreferences(context)) {
return ShareConstants.ERROR_PATCH_DISABLE;
}
File file = new File(path);
if (!SharePatchFileUtil.isLegalFile(file)) {
return ShareConstants.ERROR_PATCH_NOTEXIST;
}
//patch service can not send request
if (manager.isPatchProcess()) {
return ShareConstants.ERROR_PATCH_INSERVICE;
}
//if the patch service is running, pending
if (TinkerServiceInternals.isTinkerPatchServiceRunning(context)) {
return ShareConstants.ERROR_PATCH_RUNNING;
}
if (ShareTinkerInternals.isVmJit()) {
return ShareConstants.ERROR_PATCH_JIT;
}
Tinker tinker = Tinker.with(context);
if (tinker.isTinkerLoaded()) {
TinkerLoadResult tinkerLoadResult = tinker.getTinkerLoadResultIfPresent();
if (tinkerLoadResult != null && !tinkerLoadResult.useInterpretMode) {
String currentVersion = tinkerLoadResult.currentVersion;
if (patchMd5.equals(currentVersion)) {
return ShareConstants.ERROR_PATCH_ALREADY_APPLY;
}
}
}
if (!UpgradePatchRetry.getInstance(context).onPatchListenerCheck(patchMd5)) {
return ShareConstants.ERROR_PATCH_RETRY_COUNT_LIMIT;
}
return ShareConstants.ERROR_PATCH_OK;
}
}
```
分析onPatchReceived方法可以知道最核心的是调用了TinkerPatchService的runPatchService:
```java
TinkerPatchService.runPatchService(context, path);
```
TinkerPatchService的runPatchService方法完成了Patch的加载以及合并。TinkerPatchService的具体实现如下:
```java
public class TinkerPatchService extends IntentService {
public TinkerPatchService() {
super(com.tencent.tinker.lib.service.TinkerPatchService.class.getSimpleName());
}
//开启TinkerPatchService
public static void runPatchService(Context context, String path) {
try {
Intent intent = new Intent(context, com.tencent.tinker.lib.service.TinkerPatchService.class);
intent.putExtra(PATCH_PATH_EXTRA, path);
intent.putExtra(RESULT_CLASS_EXTRA, resultServiceClass.getName());
context.startService(intent);
} catch (Throwable throwable) {
TinkerLog.e(TAG, "start patch service fail, exception:" + throwable);
}
}
//设置upgradePatch
public static void setPatchProcessor(AbstractPatch upgradePatch, Class<? extends AbstractResultService> serviceClass) {
upgradePatchProcessor = upgradePatch;
resultServiceClass = serviceClass;
try {
Class.forName(serviceClass.getName());
} catch (ClassNotFoundException e) {
}
}
//最核心的加载与合并Patch的逻辑
@Override
protected void onHandleIntent(Intent intent) {
final Context context = getApplicationContext();
Tinker tinker = Tinker.with(context);
//开始PatchReporter,开始Patch的加载
tinker.getPatchReporter().onPatchServiceStart(intent);
if (intent == null) {
TinkerLog.e(TAG, "TinkerPatchService received a null intent, ignoring.");
return;
}
String path = getPatchPathExtra(intent);
if (path == null) {
TinkerLog.e(TAG, "TinkerPatchService can't get the path extra, ignoring.");
return;
}
File patchFile = new File(path);
long begin = SystemClock.elapsedRealtime();
boolean result;
long cost;
Throwable e = null;
increasingPriority();
PatchResult patchResult = new PatchResult();
try {
if (upgradePatchProcessor == null) {
throw new TinkerRuntimeException("upgradePatchProcessor is null.");
}
//Patch合并的逻辑都在tryPatch里面
result = upgradePatchProcessor.tryPatch(context, path, patchResult);
} catch (Throwable throwable) {
e = throwable;
result = false;
tinker.getPatchReporter().onPatchException(patchFile, e);
}
cost = SystemClock.elapsedRealtime() - begin;
tinker.getPatchReporter().
onPatchResult(patchFile, result, cost);
patchResult.isSuccess = result;
patchResult.rawPatchFilePath = path;
patchResult.costTime = cost;
patchResult.e = e;
//处理结果,这里可以进行扩展,例如防止应用重启
AbstractResultService.runResultService(context, patchResult, getPatchResultExtra(intent));
}
}
```
TinkerPatchService里面有几个很重要的方法,笔者都分别加上注释了。最核心的加载与合并Patch的逻辑onHandleIntent里面的AbstractPatch.tryPatch方法。
而AbstractPatch的具体默认实现是UpgradePatch(从前面的install方法可以看出来),下面将进入Tinker的核心逻辑,也就是UpgradePatch的tryPatch方法:
```java
public class UpgradePatch extends AbstractPatch {
private static final String TAG = "Tinker.UpgradePatch";
@Override
public boolean tryPatch(Context context, String tempPatchPath, PatchResult patchResult) {
Tinker manager = Tinker.with(context);
final File patchFile = new File(tempPatchPath);
if (!manager.isTinkerEnabled() || !ShareTinkerInternals.isTinkerEnableWithSharedPreferences(context)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:patch is disabled, just return");
return false;
}
if (!SharePatchFileUtil.isLegalFile(patchFile)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:patch file is not found, just return");
return false;
}
//check the signature, we should create a new checker
ShareSecurityCheck signatureCheck = new ShareSecurityCheck(context);
int returnCode = ShareTinkerInternals.checkTinkerPackage(context, manager.getTinkerFlags(), patchFile, signatureCheck);
if (returnCode != ShareConstants.ERROR_PACKAGE_CHECK_OK) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:onPatchPackageCheckFail");
manager.getPatchReporter().onPatchPackageCheckFail(patchFile, returnCode);
return false;
}
String patchMd5 = SharePatchFileUtil.getMD5(patchFile);
if (patchMd5 == null) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:patch md5 is null, just return");
return false;
}
//use md5 as version
patchResult.patchVersion = patchMd5;
TinkerLog.i(TAG, "UpgradePatch tryPatch:patchMd5:%s", patchMd5);
//check ok, we can real recover a new patch
final String patchDirectory = manager.getPatchDirectory().getAbsolutePath();
File patchInfoLockFile = SharePatchFileUtil.getPatchInfoLockFile(patchDirectory);
File patchInfoFile = SharePatchFileUtil.getPatchInfoFile(patchDirectory);
SharePatchInfo oldInfo = SharePatchInfo.readAndCheckPropertyWithLock(patchInfoFile, patchInfoLockFile);
//it is a new patch, so we should not find a exist
SharePatchInfo newInfo;
//already have patch
if (oldInfo != null) {
if (oldInfo.oldVersion == null || oldInfo.newVersion == null || oldInfo.oatDir == null) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:onPatchInfoCorrupted");
manager.getPatchReporter().onPatchInfoCorrupted(patchFile, oldInfo.oldVersion, oldInfo.newVersion);
return false;
}
if (!SharePatchFileUtil.checkIfMd5Valid(patchMd5)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:onPatchVersionCheckFail md5 %s is valid", patchMd5);
manager.getPatchReporter().onPatchVersionCheckFail(patchFile, oldInfo, patchMd5);
return false;
}
// if it is interpret now, use changing flag to wait main process
final String finalOatDir = oldInfo.oatDir.equals(ShareConstants.INTERPRET_DEX_OPTIMIZE_PATH)
? ShareConstants.CHANING_DEX_OPTIMIZE_PATH : oldInfo.oatDir;
newInfo = new SharePatchInfo(oldInfo.oldVersion, patchMd5, Build.FINGERPRINT, finalOatDir);
} else {
newInfo = new SharePatchInfo("", patchMd5, Build.FINGERPRINT, ShareConstants.DEFAULT_DEX_OPTIMIZE_PATH);
}
//it is a new patch, we first delete if there is any files
//don't delete dir for faster retry
// SharePatchFileUtil.deleteDir(patchVersionDirectory);
final String patchName = SharePatchFileUtil.getPatchVersionDirectory(patchMd5);
final String patchVersionDirectory = patchDirectory + "/" + patchName;
TinkerLog.i(TAG, "UpgradePatch tryPatch:patchVersionDirectory:%s", patchVersionDirectory);
//copy file
File destPatchFile = new File(patchVersionDirectory + "/" + SharePatchFileUtil.getPatchVersionFile(patchMd5));
try {
// check md5 first
if (!patchMd5.equals(SharePatchFileUtil.getMD5(destPatchFile))) {
SharePatchFileUtil.copyFileUsingStream(patchFile, destPatchFile);
TinkerLog.w(TAG, "UpgradePatch copy patch file, src file: %s size: %d, dest file: %s size:%d", patchFile.getAbsolutePath(), patchFile.length(),
destPatchFile.getAbsolutePath(), destPatchFile.length());
}
} catch (IOException e) {
// e.printStackTrace();
TinkerLog.e(TAG, "UpgradePatch tryPatch:copy patch file fail from %s to %s", patchFile.getPath(), destPatchFile.getPath());
manager.getPatchReporter().onPatchTypeExtractFail(patchFile, destPatchFile, patchFile.getName(), ShareConstants.TYPE_PATCH_FILE);
return false;
}
//we use destPatchFile instead of patchFile, because patchFile may be deleted during the patch process
if (!DexDiffPatchInternal.tryRecoverDexFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch dex failed");
return false;
}
if (!BsDiffPatchInternal.tryRecoverLibraryFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch library failed");
return false;
}
if (!ResDiffPatchInternal.tryRecoverResourceFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch resource failed");
return false;
}
// check dex opt file at last, some phone such as VIVO/OPPO like to change dex2oat to interpreted
if (!DexDiffPatchInternal.waitAndCheckDexOptFile(patchFile, manager)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, check dex opt file failed");
return false;
}
if (!SharePatchInfo.rewritePatchInfoFileWithLock(patchInfoFile, newInfo, patchInfoLockFile)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, rewrite patch info failed");
manager.getPatchReporter().onPatchInfoCorrupted(patchFile, newInfo.oldVersion, newInfo.newVersion);
return false;
}
TinkerLog.w(TAG, "UpgradePatch tryPatch: done, it is ok");
return true;
}
}
```
###Tinker核心逻辑
前面都是一些MD5、签名的校验,最核心的是:
```java
//we use destPatchFile instead of patchFile, because patchFile may be deleted during the patch process
if (!DexDiffPatchInternal.tryRecoverDexFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch dex failed");
return false;
}
if (!BsDiffPatchInternal.tryRecoverLibraryFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch library failed");
return false;
}
if (!ResDiffPatchInternal.tryRecoverResourceFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch resource failed");
return false;
}
// check dex opt file at last, some phone such as VIVO/OPPO like to change dex2oat to interpreted
if (!DexDiffPatchInternal.waitAndCheckDexOptFile(patchFile, manager)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, check dex opt file failed");
return false;
}
if (!SharePatchInfo.rewritePatchInfoFileWithLock(patchInfoFile, newInfo, patchInfoLockFile)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, rewrite patch info failed");
manager.getPatchReporter().onPatchInfoCorrupted(patchFile, newInfo.oldVersion, newInfo.newVersion);
return false;
}
```
这里分别调用不同的Diff算法,处理了Dex(代码)、so、资源等。这里以Dex文件的处理为例进行讲解,一路分析tryRecoverDexFiles,最终会发现是调用了DexPatchApplier的executeAndSaveTo方法:
```java
public void executeAndSaveTo(OutputStream out) throws IOException {
// Before executing, we should check if this patch can be applied to
// old dex we passed in.
byte[] oldDexSign = this.oldDex.computeSignature(false);
if (oldDexSign == null) {
throw new IOException("failed to compute old dex's signature.");
}
if (this.patchFile == null) {
throw new IllegalArgumentException("patch file is null.");
}
byte[] oldDexSignInPatchFile = this.patchFile.getOldDexSignature();
if (CompareUtils.uArrCompare(oldDexSign, oldDexSignInPatchFile) != 0) {
throw new IOException(
String.format(
"old dex signature mismatch! expected: %s, actual: %s",
Arrays.toString(oldDexSign),
Arrays.toString(oldDexSignInPatchFile)
)
);
}
// Firstly, set sections' offset after patched, sort according to their offset so that
// the dex lib of aosp can calculate section size.
TableOfContents patchedToc = this.patchedDex.getTableOfContents();
patchedToc.header.off = 0;
patchedToc.header.size = 1;
patchedToc.mapList.size = 1;
//获取Patch文件中的Dex文件的分区以及子分区的相关偏移量
patchedToc.stringIds.off
= this.patchFile.getPatchedStringIdSectionOffset();
patchedToc.typeIds.off
= this.patchFile.getPatchedTypeIdSectionOffset();
patchedToc.typeLists.off
= this.patchFile.getPatchedTypeListSectionOffset();
patchedToc.protoIds.off
= this.patchFile.getPatchedProtoIdSectionOffset();
patchedToc.fieldIds.off
= this.patchFile.getPatchedFieldIdSectionOffset();
patchedToc.methodIds.off
= this.patchFile.getPatchedMethodIdSectionOffset();
patchedToc.classDefs.off
= this.patchFile.getPatchedClassDefSectionOffset();
patchedToc.mapList.off
= this.patchFile.getPatchedMapListSectionOffset();
patchedToc.stringDatas.off
= this.patchFile.getPatchedStringDataSectionOffset();
patchedToc.annotations.off
= this.patchFile.getPatchedAnnotationSectionOffset();
patchedToc.annotationSets.off
= this.patchFile.getPatchedAnnotationSetSectionOffset();
patchedToc.annotationSetRefLists.off
= this.patchFile.getPatchedAnnotationSetRefListSectionOffset();
patchedToc.annotationsDirectories.off
= this.patchFile.getPatchedAnnotationsDirectorySectionOffset();
patchedToc.encodedArrays.off
= this.patchFile.getPatchedEncodedArraySectionOffset();
patchedToc.debugInfos.off
= this.patchFile.getPatchedDebugInfoSectionOffset();
patchedToc.codes.off
= this.patchFile.getPatchedCodeSectionOffset();
patchedToc.classDatas.off
= this.patchFile.getPatchedClassDataSectionOffset();
patchedToc.fileSize
= this.patchFile.getPatchedDexSize();
Arrays.sort(patchedToc.sections);
patchedToc.computeSizesFromOffsets();
//获取存在BUG的Dex文件的分区以及子分区的相关偏移量
// Secondly, run patch algorithms according to sections' dependencies.
this.stringDataSectionPatchAlg = new StringDataSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToPatchedIndexMap
);
this.typeIdSectionPatchAlg = new TypeIdSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToPatchedIndexMap
);
this.protoIdSectionPatchAlg = new ProtoIdSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToPatchedIndexMap
);
this.fieldIdSectionPatchAlg = new FieldIdSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToPatchedIndexMap
);
this.methodIdSectionPatchAlg = new MethodIdSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToPatchedIndexMap
);
this.classDefSectionPatchAlg = new ClassDefSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToPatchedIndexMap
);
this.typeListSectionPatchAlg = new TypeListSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToPatchedIndexMap
);
this.annotationSetRefListSectionPatchAlg = new AnnotationSetRefListSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToPatchedIndexMap
);
this.annotationSetSectionPatchAlg = new AnnotationSetSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToPatchedIndexMap
);
this.classDataSectionPatchAlg = new ClassDataSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToPatchedIndexMap
);
this.codeSectionPatchAlg = new CodeSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToPatchedIndexMap
);
this.debugInfoSectionPatchAlg = new DebugInfoItemSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToPatchedIndexMap
);
this.annotationSectionPatchAlg = new AnnotationSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToPatchedIndexMap
);
this.encodedArraySectionPatchAlg = new StaticValueSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToPatchedIndexMap
);
this.annotationsDirectorySectionPatchAlg = new AnnotationsDirectorySectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToPatchedIndexMap
);
//将Patch文件中的Dex文件的分区以及子分区的相关偏移量应用到有BUG的Dex文件中
this.stringDataSectionPatchAlg.execute();
this.typeIdSectionPatchAlg.execute();
this.typeListSectionPatchAlg.execute();
this.protoIdSectionPatchAlg.execute();
this.fieldIdSectionPatchAlg.execute();
this.methodIdSectionPatchAlg.execute();
this.annotationSectionPatchAlg.execute();
this.annotationSetSectionPatchAlg.execute();
this.annotationSetRefListSectionPatchAlg.execute();
this.annotationsDirectorySectionPatchAlg.execute();
this.debugInfoSectionPatchAlg.execute();
this.codeSectionPatchAlg.execute();
this.classDataSectionPatchAlg.execute();
this.encodedArraySectionPatchAlg.execute();
this.classDefSectionPatchAlg.execute();
// Thirdly, write header, mapList. Calculate and write patched dex's sign and checksum.
Dex.Section headerOut = this.patchedDex.openSection(patchedToc.header.off);
patchedToc.writeHeader(headerOut);
Dex.Section mapListOut = this.patchedDex.openSection(patchedToc.mapList.off);
patchedToc.writeMap(mapListOut);
this.patchedDex.writeHashes();
// Finally, write patched dex to file.
this.patchedDex.writeTo(out);
}
```
这里干了3个核心逻辑:
1. 获取Patch文件中的Dex文件的分区以及子分区的相关偏移量
2. 获取存在BUG的Dex文件的分区以及子分区的相关偏移量
3. 将Patch文件中的Dex文件的分区以及子分区的相关偏移量通过不同的算法应用到有BUG的Dex文件中
其余的so、资源文件的处理都是分别通过不同Diff算法进行处理的,这里就不再赘述了,原理都差不多。
###结语
到此为止,Tinker的核心流程就分析完毕了。在这次源码分析中,我们学会了Tinker是如何利用单例模式、Builder模式等设计模式进行设计,然后也学习到了不同类之间是如何分工合作的。当然最重要的是我们掌握了Tinker最核心的Diff算法。