Tinker合并流程

1. TinkerInstaller # install()
TinkerInstaller主要提供了两个install()方法,一个简单的,另一个复杂一点的适用于需要自定义更多功能的。
1-1 多参数的install() 先看这个多参数的install(),主要:

  1. 使用传入参数构建了一个Tinker对象,Tinker使用了构建者模式。
  2. 调用了Tinker的create()和install()。
public static Tinker install(ApplicationLike applicationLike, LoadReporter loadReporter, PatchReporter patchReporter, PatchListener listener, Class 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; }

1-2 单参数的install() 单参数的install()也是相同的,只是构建Tinker对象时没有设置那些传入的参数。
public static Tinker install(ApplicationLike applicationLike) { Tinker tinker = new Tinker.Builder(applicationLike.getApplication()).build(); Tinker.create(tinker); tinker.install(applicationLike.getTinkerResultIntent()); return tinker; }

这里就能看出来TinkerInstaller是一个外观模式,并没有执行初始化的工作,真正工作的是Tinker类,所以我们来看Tinker。
1-3 Tinker的单例模式 Tinker类是一个单例,用得是DCL,没有处理DCL失效问题,可能是因为发生的概率太小了且处理会让效率降低。最后是用Builder去构造对象,使用了构建者模式,Builder是Tinker内部类,去管理Tinker的参数。
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; }

Builder在Tinker的内部,统一管理了一些参数,包括多参数install()传入的Reporter和Listener等。
private final Context context; private final boolean mainProcess; private final boolean patchProcess; private int status = -1; private LoadReporterloadReporter; private PatchReporter patchReporter; private PatchListener listener; private FilepatchDirectory; private FilepatchInfoFile; private FilepatchInfoLockFile; private BooleantinkerLoadVerifyFlag;

1-4 Builder的构造器 在构造方法中会先初始化一些,主要是context所在线程的判断和各种目录的初始。
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); }

1-5 Builder # patchReporter() 之后就是构建者模式,提供了一系列设置参数的方法。比如这个设置patchReporter的方法。设置的方法不要多次调用,并不会覆盖掉之前的设置,只会抛已经设置过的异常。
public Builder patchReporter(PatchReporter patchReporter) { if (patchReporter == null) { throw new TinkerRuntimeException("patchReporter must not be null."); } if (this.patchReporter != null) { throw new TinkerRuntimeException("patchReporter is already set."); } this.patchReporter = patchReporter; return this; }

1-6 Builder # build() 在build()中会判空,如果是之前还没有初始赋值的参数,就赋默认值。最后调用Tinker构造器传入初始化的参数创建Tinker对象并返回。
public Tinker build() { if (status == -1) { status = ShareConstants.TINKER_ENABLE_ALL; } // 如果调用的是但参数的install()就会在这里赋默认值 if (loadReporter == null) { loadReporter = new DefaultLoadReporter(context); } if (patchReporter == null) { patchReporter = new DefaultPatchReporter(context); } if (listener == null) { listener = new DefaultPatchListener(context); } if (tinkerLoadVerifyFlag == null) { tinkerLoadVerifyFlag = false; } return new Tinker(context, status, loadReporter, patchReporter, listener, patchDirectory, patchInfoFile, patchInfoLockFile, mainProcess, patchProcess, tinkerLoadVerifyFlag); }

1-7 Tinker # create() 回到1-2,Tinker对象初始化好后传入Tinker的create(),来看create()。这个方法就是赋值单例sInstance。
public static void create(Tinker tinker) { if (sInstance != null) { throw new TinkerRuntimeException("Tinker instance is already set."); } sInstance = tinker; }

1-8 Tinker # install() 1-2在调用完create()后就会调用install(),Tinker的install()也有两个,单参数的TinkerInstaller#install()调用单参数的install(),多参数的调用多参数的,单参数也是调用多参数的install(),后面两个参数就生成默认对象传入了。
public void install(Intent intentResult) { install(intentResult, DefaultTinkerResultService.class, new UpgradePatch()); }

再看多参数的install(),它完成真正install的逻辑。主要工作:
  1. 置标记sInstalled为true。
  2. 将两个参数注入到TinkerPatchService中。
  3. 初始化TinkerLoadResult,调用onLoadResult()。
public void install(Intent intentResult, Class 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!"); } }

2. TinkerInstaller # onReceiveUpgradePatch()
同样因为外观模式,所以TinkerInstaller没有任何处理,直接交给Tinker对象中已经初始化好的PatchListener的onPatchReceived()处理。
public static void onReceiveUpgradePatch(Context context, String patchLocation) { Tinker.with(context).getPatchListener().onPatchReceived(patchLocation); }

2-1 PatchListener # onPatchReceived() PatchListener是一个接口,只有一个onPatchReceived()接口方法。DefaultPatchListener是PatchListener实现类。
public interface PatchListener { int onPatchReceived(String path); }

2-2 DefaultPatchListener # onPatchReceived() 可以自己去实现PatchListener,但总归需要处理的工作是相似的。我们看一个默认实现,DefaultPatchListener。在onPatchReceived()方法里:
  1. 调用patchCheck()对patch文件进行一系列的安全性检查,去重写这个方法也就可以实现自己的检查逻辑了。
  2. 如果检查是安全地就开启TinkerPatchService,开始合并补丁包。
  3. 如果检查没通过就调用LoadReporter的相关方法通知。
@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; }

到这里中心就要转去TinkerPatchService了。
总结一下:
  1. TinkerInstaller使用了外观模式,没有真正逻辑处理,只是封装了Tinker的各种调用,真正处理的逻辑在Tinker中。
  2. Tinker使用了单例模式(DCL) + 构建者模式。
  3. TinkerInstaller提供了两个api:
    • install()用来创建并初始化Tinker对象,并调用了Tinker对象的create()和install()。
    • onReceiveUpgradePatch()中调用了Tinker对象中PatchListener的onPatchReceived()。
  4. Tinker的install()中也初始了TinkerPatchService,为后面做准备。
  5. PatchListener的onPatchReceived()用来检查patch文件合法性并开启执行修复工作的TinkerPatchService。
3. TinkerPatchService
TinkerPatchService就是加载合并patch文件的Service,继承了IntentService。
3-1 TinkerPatchService # setPatchProcessor() 回顾一下Tinker的install(),在install()中就传入了两个参数调用了setPatchProcessor()。再追溯参数来源其实是在我们自己编写代码调用TinkerInstaller的复杂install()传入的或是调用简单install()时Tinker的Builder为我们默认创建的。
多参数的源头:
// 回顾我们自己编写的代码,调用 TinkerInstaller#install()。 // 最终最后两个参数被注入到TinkerPatchService中。 AbstractPatch upgradePatchProcessor = new UpgradePatch(); TinkerInstaller.install(mAppLike, loadReporter, patchReporter, mPatchListener, CustomResultService.class,// 决定在patch文件安装完毕后的操作 upgradePatchProcessor// 决定patch文件的安装策略 ); // 复杂的初始化方法

单参数的源头:
// Tinker # install() public void install(Intent intentResult) { // 传入的是为我们默认实例的对象。 install(intentResult, DefaultTinkerResultService.class, new UpgradePatch()); }

一直传递到setPatchProcessor(),最后向TinkerPatchService注入了这两个对象。
  • upgradePatch是我们直接创建的UpgradePatch()对象,表示patch文件的安装策略。
  • serviceClass是我们自定义的DefaultTinkerResultService类,表示修复完毕后的动作。
public static void setPatchProcessor(AbstractPatch upgradePatch, Class serviceClass) { upgradePatchProcessor = upgradePatch; resultServiceClass = serviceClass; //try to load try { Class.forName(serviceClass.getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } }

3-2 TinkerPatchService # runPatchService() 接着上面看一下runPatchService(),就是启动TinkerPatchService的代码,intent中还传了一个patch文件路径path和一个在Tinker#install()中传入的一个ResultService类名。
public static void runPatchService(Context context, String path) { try { Intent intent = new Intent(context, 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); } }

3-3 TinkerPatchService # onHandleIntent()
  1. 在这个方法中最重要的就是调用了tryPatch(),也就是更近一步的修复逻辑,因为onHandleIntent()是支持耗时操作的,所以完全可以猜想tryPatch()是同步方法。
  2. 其次重要就是在处理完tryPatch()之后会开启ResultService,执行修复完毕后的工作。
@Override protected void onHandleIntent(Intent intent) { final Context context = getApplicationContext(); Tinker tinker = Tinker.with(context); // PatchReporter回调 tinker.getPatchReporter().onPatchServiceStart(intent); // ...一些异常判断处理...// 提升进程优先级,尽可能保证此Service不被kill increasingPriority(); PatchResult patchResult = new PatchResult(); try { if (upgradePatchProcessor == null) { throw new TinkerRuntimeException("upgradePatchProcessor is null."); } // 核心调用tryPatch() result = upgradePatchProcessor.tryPatch(context, path, patchResult); } catch (Throwable throwable) { e = throwable; result = false; // PatchReporter回调 tinker.getPatchReporter().onPatchException(patchFile, e); }cost = SystemClock.elapsedRealtime() - begin; // PatchReporter回调 tinker.getPatchReporter().onPatchResult(patchFile, result, cost); patchResult.isSuccess = result; patchResult.rawPatchFilePath = path; patchResult.costTime = cost; patchResult.e = e; // 开始执行ResultService,执行修复完毕后的工作 AbstractResultService.runResultService(context, patchResult, getPatchResultExtra(intent)); }

3-4 TinkerPatchService # increasingPriority() 在说两个核心过程之前先来看这个方法,这个方法在tryPatch()之前被调用,主要是利用系统漏洞让Service优先级高一些避免轻易被回收。
private void increasingPriority() { TinkerLog.i(TAG, "try to increase patch process priority"); try { Notification notification = new Notification(); if (Build.VERSION.SDK_INT < 18) { startForeground(notificationId, notification); } else { startForeground(notificationId, notification); // start InnerService startService(new Intent(this, InnerService.class)); } } catch (Throwable e) { TinkerLog.i(TAG, "try to increase patch process priority error:" + e); } }

public static class InnerService extends Service { @Override public void onCreate() { super.onCreate(); try { startForeground(notificationId, new Notification()); } catch (Throwable e) { TinkerLog.e(TAG, "InnerService set service for push exception:%s.", e); } // kill stopSelf(); }@Override public void onDestroy() { stopForeground(true); super.onDestroy(); }@Override public IBinder onBind(Intent intent) { return null; } }

4. UpgradePatch # tryPatch()
在上面分析的TinkerPatchService#onHanleIntent():
result = upgradePatchProcessor.tryPatch(context, path, patchResult);

4-1 AbstractPatch upgradePatchProcessor是AbstractPatch类的,只有一个抽象方法tryPatch()。
public abstract class AbstractPatch {public abstract boolean tryPatch(Context context, String tempPatchPath, PatchResult patchResult); }

upgradePatchProcessor是AbstractPatch的实例对象,在Tinker的install()调用时传入,也就是多参数TinkerInstaller的install()传入的,实现类是UpgradePatch,看tryPatch()实现,真的是非常长,我们分成两个部分来看,显示检查再是算法调用。
4-2 UpgradePatch # tryPatch()中的检查逻辑 tryPatch()的返回值便是合成Patch成功与否,在方法的开始都是一些判断,对Tinker的检查、文件的检查、签名检查、TinkerId检查、文件md5检查等,一旦检查不安全就直接返回false。
@Override public boolean tryPatch(Context context, String tempPatchPath, PatchResult patchResult) { Tinker manager = Tinker.with(context); final File patchFile = new File(tempPatchPath); // 检查Tinker参数和SharedPreferences是否可用 if (!manager.isTinkerEnabled() || !ShareTinkerInternals.isTinkerEnableWithSharedPreferences(context)) { TinkerLog.e(TAG, "UpgradePatch tryPatch:patch is disabled, just return"); return false; }// 判断patch存在、可读、是文件、大小大于0 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); // 解压patch去检查签名、TinkerId、和patch压缩包中文件全不全 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; }// 获取patch的md5 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 // 存patch文件的md5 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); // 看之前是不是有patch SharePatchInfo oldInfo = SharePatchInfo.readAndCheckPropertyWithLock(patchInfoFile, patchInfoLockFile); //it is a new patch, so we should not find a exist SharePatchInfo newInfo; //already have patch // 构建newInfo 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; } // 检查md5不空且长度正确 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); }// ......//copy file File destPatchFile = new File(patchVersionDirectory + "/" + SharePatchFileUtil.getPatchVersionFile(patchMd5)); try { // check md5 first if (!patchMd5.equals(SharePatchFileUtil.getMD5(destPatchFile))) { // 检查md5正确后拷贝文件,因为后面操作可能会发生以外而删除patch文件。 // 所以在这里拷贝一份,后面的操作对拷贝的patch来操作。 SharePatchFileUtil.copyFileUsingStream(patchFile, destPatchFile); TinkerLog.w(TAG, "UpgradePatch copy patch file, src file: %s size: %d, dest file: %s size:%d", patchFile.getAbsolutePath(), patchFile.lengt 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; }// ......检查成功的后序合并算法调用}

4-3 UpgradePatch # tryPatch()中合并算法的调用 在通过了这一系列检查之后就到了真正合并文件算法的时候了,合并的文件分为三种:dex文件、.so文件和资源文件分别对应下面三个调用,只要一个修复工作失败了,就返回false,修复算法我们在下一篇分析。
@Override public boolean tryPatch(Context context, String tempPatchPath, PatchResult patchResult) {// ......一系列检查工作//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; } // ......写新的合并Patch信息 }

5. AbstractResultService
回顾3-3,在tryPatch()调用完成后,最后一句:
AbstractResultService.runResultService(context, patchResult, getPatchResultExtra(intent));

开启了修复完成后的工作Service,DefaultTinkerResultService是默认实现,或者也可以自定义,在TinkerInstaller#install()传入。
5-1 AbstractResultService # runResultService() resultServiceClass是一路传递过来的类名,到这里就是启动了,默认给的是DefaultTinkerResultService
public static void runResultService(Context context, PatchResult result, String resultServiceClass) { if (resultServiceClass == null) { throw new TinkerRuntimeException("resultServiceClass is null."); } try { Intent intent = new Intent(); intent.setClassName(context, resultServiceClass); intent.putExtra(RESULT_EXTRA, result); context.startService(intent); } catch (Throwable throwable) { TinkerLog.e(TAG, "run result service fail, exception:" + throwable); } }

5-2 DefaultTinkerResultService # onHandleIntent() DefaultTinkerResultService没有重写该方法,父类实现直接调用的onPatchResult()
@Override protected void onHandleIntent(Intent intent) { if (intent == null) { TinkerLog.e(TAG, "AbstractResultService received a null intent, ignoring."); return; } PatchResult result = (PatchResult) ShareIntentUtil.getSerializableExtra(intent, RESULT_EXTRA); onPatchResult(result); }

5-3 DefaultTinkerResultService # onPatchResult() 【Tinker合并流程】首先会关闭PatchService,然后删除patch文件,最后将应用进程杀死,再开就生效了。但是这样的体验不好,所以如果想要自己的逻辑,就可以自定义DefaultTinkerResultService,重写onPatchService()。
@Override public void onPatchResult(PatchResult result) {// ......一些判断和日志打印//first, we want to kill the recover process // 关闭TinkerPatchService TinkerServiceInternals.killTinkerPatchServiceProcess(getApplicationContext()); // if success and newPatch, it is nice to delete the raw file, and restart at once // only main process can load an upgrade patch! if (result.isSuccess) { // 如果修复成功了,就把patch删掉 deleteRawPatchFile(new File(result.rawPatchFilePath)); if (checkIfNeedKill(result)) { // 这就是为什么不自定义ResultService时,修复完成应用会闪退 android.os.Process.killProcess(android.os.Process.myPid()); } else { TinkerLog.i(TAG, "I have already install the newly patch version!"); } } }

到这里整个流程就结束了,默认的话此时进程已被杀死,再次启动才能够生效。就再分析分析启动过程中发生的事情。

    推荐阅读