引言
上次我们分析了Tinker中关于Application的代理机制,这次我们来分析一下Tinker是如何进行补丁的合成与加载的。
上一篇我们讲了 Tinker源码解析系列—Application代理机制
阅读本文可能需要花费10分钟。
以下所有对源码的分析均基于Tinker 1.7.7 版本,且由于源码过长,均只贴出关键部分
Tinker中比较关键的几个类
在开始分析补丁合成和加载过程中,我们先来看看在Tinker中比较重要的几个类。
PatchListener
Patch监听者,负责执行获取到补丁包后的后续操作LoadReporter
Load记录者,负责记录加载补丁包过程的状态信息PatchReporter
Patch记录者,负责记录合成补丁过程的状态信息AbstractPatch
Patch执行者,负责执行补丁的合成
其中,PatchListener
,LoadReporter
,PatchReporter
均为接口,AbstractPatch
为抽象类,在Tinker中他们都有默认的实现类,对应关系如下:PatchListener
默认实现类为DefaultPatchListener
LoadReporter
默认实现类为DefaultLoadReporter
PatchReporter
默认实现类为DefaultPatchReporter
AbstractPatch
默认实现类为UpgradePatch
Tinker的成员变量中包含了PatchListener
, LoadReporter
,PatchReporter
public class Tinker {
...
final PatchListener listener;
final LoadReporter loadReporter;
final PatchReporter patchReporter;
...
}
在Tinker中采用了Builder模式进行构造,我们直接看一下源码是怎么对他们进行赋值的
public static class Builder {
public Tinker build() {
...
if (loadReporter == null) {
loadReporter = new DefaultLoadReporter(context);
}
if (patchReporter == null) {
patchReporter = new DefaultPatchReporter(context);
}
if (listener == null) {
listener = new DefaultPatchListener(context);
}
...
return new Tinker(context, status, loadReporter, patchReporter, listener, patchDirectory,
patchInfoFile, patchInfoLockFile, mainProcess, patchProcess, tinkerLoadVerifyFlag);
}
}
看在这里,为什么我们没有发现AbstractPatch
的影子呀?其实它是在install
方法中进行设置的,我们直接看一下源码,
public class Tinker {
...
public void install(Intent intentResult) {
install(intentResult, DefaultTinkerResultService.class, new UpgradePatch());
}
...
}
所以说Tinker提供了强大的自定义能力,我们完全能够去继承这几个类从而实现我们一些自定义的需求,好了不扯远了,这里我们大概清楚了这几个类的作用,接下来我们就进入正题
补丁的合成
在Tinker的官方Demo中,补丁合成调用的方法是
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk");
我们点击该方法进去,
public class TinkerInstaller {
public static void onReceiveUpgradePatch(Context context, String patchLocation) {
Tinker.with(context).getPatchListener().onPatchReceived(patchLocation);
}
}
可以看到,实际上是调用了PatchListener的onPatchReceived
方法,我们前面说过,在Tinker中,如果我们没有自定义PatchListener,那么默认的PatchListener就是DefaultPatchListener
,这里我们直接去到DefaultPatchListener
查看它的onPatchReceived
方法
public int onPatchReceived(String path) {
//对补丁路径进行校验,进行路径是否存在等等进行判断
int returnCode = patchCheck(path);
if (returnCode == ShareConstants.ERROR_PATCH_OK) {
//校验成功,开启Patch服务
TinkerPatchService.runPatchService(context, path);
} else {
//校验失败,交给LoadReporter加载记录者进行状态记录
Tinker.with(context).getLoadReporter().onLoadPatchListenerReceiveFail(new File(path), returnCode);
}
return returnCode;
}
可以看到,补丁文件检验成功后会通过TinkerPatchService.runPatchService(context, path)
运行TinkerPatchService,代码很简单
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);
}
}
就是将补丁路径和resultServiceClass通过Intent传递,其中resultServiceClass是一个合成补丁结束后执行的Service,它的赋值也在Tinker的install
方法中执行,我们后面会讲到它
TinkerPatchService继承了IntentService,这里我们直接看看它的onHandleIntent
方法,
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.");
}
//最关键的一步,执行合成补丁!!!!!!
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;
//合成补丁结束,运行合成补丁后的Service
AbstractResultService.runResultService(context, patchResult, getPatchResultExtra(intent));
}
别看代码长,其实最核心的就是upgradePatchProcessor.tryPatch(context, path, patchResult)
而已,其中upgradePatchProcessor
其实就是AbstractPatch
的实现者,在Tinker的install
方法中被赋值,默认为UpgradePatch
,这我们前面已经分析过了,那么,合成补丁的过程,实际上就是在UpgradePatch
的tryPatch
方法之中了
这里穿插一个比较有意思的细节,就是上面的 increasingPriority()
方法,看方法名的意思大概是提升权重的意思,我们来看一下代码
private void increasingPriority() {
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);
}
}
现在大家肯定对这段代码不陌生,就是利用系统漏洞进行保活~ 好了回归正题,我们接着看UpgradePatch
的tryPatch
方法