从加载流程来分析
处理下载之后的修复包:
/**
* new patch file to install, try install them with :patch process
* Generally you will not use it
*
* @param context
* @param patchLocation
*/
public static void onReceiveUpgradePatch(Context context, String patchLocation) {
Tinker.with(context).getPatchListener().onPatchReceived(patchLocation, true);
}
调用TinkerInstaller
的onReceiveUpgradePatch
方法,在这个方法里面初始化Tinker
类,并且调用PatchListener
的onPatchReceived(String path, boolean isUpgrade)
方法。
private Tinker(Context context, int tinkerFlags, LoadReporter loadReporter, PatchReporter patchReporter, PatchListener listener, File patchDirectory, File patchInfoFile, 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.isMainProcess = isInMainProc;
this.tinkerLoadVerifyFlag = tinkerLoadVerifyFlag;
this.isPatchProcess = isPatchProcess;
}
其中:patchDirectory是patch包的存储位置。patchInfoFile是存储Patch包版本等信息的文件。
一、对修复包的第一层验证
这是Tinker
的初始化方法,初始化了LoaderReporter
、PatchReporter
PatchListener
等。
值得注意的是Tinker
初始化之前必须得先调用TinkerInstaller
的install方法,这个后面再说。
/**
* when we receive a patch, what would we do?
* you can overwrite it
*
* @param path
* @param isUpgrade
* @return
*/
@Overridepublic int onPatchReceived(String path, boolean isUpgrade) {
int returnCode = patchCheck(path, isUpgrade);
if (returnCode == ShareConstants.ERROR_PATCH_OK) {
TinkerPatchService.runPatchService(context, path, isUpgrade);
} else {
Tinker.with(context).getLoadReporter().
onLoadPatchListenerReceiveFail(new File(path), returnCode, isUpgrade);
}
return returnCode;
}
DefaultPatchListener
中,对修复包进行检查,如果修复包ok,则开启一个单独的进程合成全量包。如果修复包不完整或者有其它问题,则调用LoadReporter
的onLoadPatchListenerReceiveFail(File patchFile, int errorCode, boolean isUpgrade)
方法通知。
patchCheck
验证了:
- Tinker开关是否开启
- 修复文件是否存在
- 是不是通过合成进程执行的操作
- 合成进程是否正在执行
第二、开启合成进程
public static void runPatchService(Context context, String path, boolean isUpgradePatch) {
Intent intent = new Intent(context, TinkerPatchService.class);
intent.putExtra(PATCH_PATH_EXTRA, path);
intent.putExtra(PATCH_NEW_EXTRA, isUpgradePatch);
context.startService(intent);
}
在TinkerPatchService
中开启合成进程的服务。它是一个IntentSerivce,是在单独的线程中进行的操作。下面来看一下onHandleIntent
中的操作。
@Overrideprotected void onHandleIntent(Intent intent) {
final Context context = getApplicationContext();
Tinker tinker = Tinker.with(context);
tinker.getPatchReporter().onPatchServiceStart(intent);
//调用patchReporter的onPatchServiceStart(Intent 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);
boolean isUpgradePatch = getPatchUpgradeExtra(intent);
long begin = SystemClock.elapsedRealtime();
boolean result;
long cost;
Throwable e = null;
increasingPriority();
PatchResult patchResult = new PatchResult();
try {
if (isUpgradePatch) {
if (upgradePatchProcessor == null) {
throw new TinkerRuntimeException("upgradePatchProcessor is null.");
}
result = upgradePatchProcessor.tryPatch(context, path, patchResult);
} else {
//just recover from exist patch
if (repairPatchProcessor == null) {
throw new TinkerRuntimeException("upgradePatchProcessor is null.");
}
result = repairPatchProcessor.tryPatch(context, path, patchResult);
}
} catch (Throwable throwable) {
e = throwable;
result = false;
tinker.getPatchReporter().onPatchException(patchFile, e, isUpgradePatch);
}
cost = SystemClock.elapsedRealtime() - begin;
tinker.getPatchReporter().
onPatchResult(patchFile, result, cost, isUpgradePatch);
patchResult.isSuccess = result;
patchResult.isUpgradePatch = isUpgradePatch;
patchResult.rawPatchFilePath = path;
patchResult.costTime = cost;
patchResult.e = e;
AbstractResultService.runResultService(context, patchResult);
}
有几个操作需要注意的:
调用
patchReporter
的onPatchServiceStart(Intent intent)
方法通知。-
让服务置于前台服务
increasingPriority
。1.
startForeground(int id, Notification notification)
这个方法,可以让后台服务置于前台,就像音乐播放器的,播放服务一样,不会被系统杀死。
2.开启一个InnerService降低被杀死的概率。 TinkerInstaller
调用的install
方法,会初始化两个AbstractPatch
,ReparePatch
和UpgradePatch
。在这里会调用它们的tryPatch(Context context, String tempPatchPath, PatchResult patchResult)
方法来执行合并操作。并将结果返回。如果两个
AbstractPatch
为空,则会捕获异常。并且调用PatchReporter
的onPatchException
来通知。修复成功后调用
PatchReporter
的onPatchResult()
来通知,有花费时间等信息。调用
AbstactResultService
的runResultService(Context context, PatchResult result)
方法。也是在TinkerInstaller
的Install
的时候赋值。也是一个IntentService
。
这个Service
会将补丁合成进程返回的结果返回给主进程,在单独的线程中执行。onHandleIntent
中只是回调了runResultService(Context context, PatchResult result)
方法。通过IntentService完成了进程间的通信。
第三、最后的合成方法
为了不至于离的太远,我们还顺着刚才的流程分析AbstractPatch
的tryPatch
方法。我们先看下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 (!patchFile.isFile() || !patchFile.exists()) {
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, true, returnCode);
return false;
}
patchResult.patchTinkerID = signatureCheck.getNewTinkerID();
patchResult.baseTinkerID = signatureCheck.getTinkerID();
//it is a new patch, so we should not find a exist
SharePatchInfo oldInfo = manager.getTinkerLoadResultIfPresent().patchInfo;
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;
SharePatchInfo newInfo;
//already have patch
if (oldInfo != null) {
if (oldInfo.oldVersion == null || oldInfo.newVersion == null) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:onPatchInfoCorrupted");
manager.getPatchReporter().onPatchInfoCorrupted(patchFile, oldInfo.oldVersion, oldInfo.newVersion, true);
return false;
}
if (oldInfo.oldVersion.equals(patchMd5) || oldInfo.newVersion.equals(patchMd5)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:onPatchVersionCheckFail");
manager.getPatchReporter().onPatchVersionCheckFail(patchFile, oldInfo, patchMd5, true);
return false;
}
newInfo = new SharePatchInfo(oldInfo.oldVersion, patchMd5); } else {
newInfo = new SharePatchInfo("", patchMd5);
}
//check ok, we can real recover a new patch
final String patchDirectory = manager.getPatchDirectory().getAbsolutePath();
TinkerLog.i(TAG, "UpgradePatch tryPatch:dexDiffMd5:%s", patchMd5);
final String patchName = SharePatchFileUtil.getPatchVersionDirectory(patchMd5);
final String patchVersionDirectory = patchDirectory + "/" + patchName;
TinkerLog.i(TAG, "UpgradePatch tryPatch:patchVersionDirectory:%s", patchVersionDirectory);
//it is a new patch, we first delete if there is any files
//don't delete dir for faster retry//
SharePatchFileUtil.deleteDir(patchVersionDirectory);
//copy file
File destPatchFile = new File(patchVersionDirectory + "/" + SharePatchFileUtil.getPatchVersionFile(patchMd5));
try {
SharePatchFileUtil.copyFileUsingStream(patchFile, destPatchFile); TinkerLog.w(TAG, "UpgradePatch after %s size:%d, %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, true);
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, true)) { TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch dex failed");
return false;
}
if (!BsDiffPatchInternal.tryRecoverLibraryFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile, true)) { TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch library failed");
return false;
}
if (!ResDiffPatchInternal.tryRecoverResourceFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile, true)) { TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch resource failed");
return false;
}
final File patchInfoFile = manager.getPatchInfoFile();
if (!SharePatchInfo.rewritePatchInfoFileWithLock(patchInfoFile, newInfo, SharePatchFileUtil.getPatchInfoLockFile(patchDirectory))) { TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, rewrite patch info failed");
manager.getPatchReporter().onPatchInfoCorrupted(patchFile, newInfo.oldVersion, newInfo.newVersion, true);
return false;
}
TinkerLog.w(TAG, "UpgradePatch tryPatch: done, it is ok"); return true;
}
方法有点长, 我们一步一步分析,里面有几个点需要注意。
-
ShareSecurityCheck
是检查签名的类,里面封闭了签名检查的方法 - ShareTinkerInternals的静态方法
checkTinkerPackage(Context context, int tinkerFlag, File patchFile, ShareSecurityCheck securityCheck)
传入了ShareSecurityCheck
,它代理了:
1.签名检查
2.TinkerId
检查
3.Tinker
开关检查,dex、resource、libriry
的meta
信息检查。
如果检查失败通过PatchReporter抛出onPatchPackageCheckFail
-SharePatchInfo
,存储了修复包的版本信息,有oldVersion
和newVersion
。newVersion
是修复包的md5
值。
4.什么时候oldInfo
会存在呢?加载成功过一次,也修复成功了。再次执行合成的时候。如果下载的包还是之前的包,则会报告onPatchVersionCheckFail
。如果是新的修复包,则会把oldVersion
赋值给SharePatchInfo(String oldVer, String newVew)
中的ondVer。 - 拷贝修复包到data/data目录
- DexDiff合成dex、BsDiff合成library、BsDiff合成res。
- 拷贝
SharePatchInfo
到PatchInfoFile
中,PatchInfoFile
在TinkerInstaller
的install
方法中初始化。
稍后再分别分析patch
包的资源合成操作。先分析一下最早调用的TinkerInstaller
的install
方法。
四、安装方法
TinkerInstaller类中有两个安装方法一个是默认的,一个是自定义属性的。
/**
* install tinker with default config, you must install tinker before you use their api
* or you can just use {@link TinkerApplicationHelper}'s api
*
* @param applicationLike
*/
public static void install(ApplicationLike applicationLike) {
Tinker tinker = new Tinker.Builder(applicationLike.getApplication()).build();
Tinker.create(tinker);
tinker.install(applicationLike.getTinkerResultIntent());
}
这个是默认的方法,自定义方法只不过install
的参数传入了自己自定义的一些类。
/**
* install tinker with custom config, you must install tinker before you use their api
* or you can just use {@link TinkerApplicationHelper}'s api *
* @param applicationLike
* @param loadReporter
* @param patchReporter
* @param listener
* @param resultServiceClass
* @param upgradePatchProcessor
* @param repairPatchProcessor
*/
public static void install(ApplicationLike applicationLike, LoadReporter loadReporter, PatchReporter patchReporter, PatchListener listener, Class extends AbstractResultService> resultServiceClass, AbstractPatch upgradePatchProcessor, AbstractPatch repairPatchProcessor) {
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, repairPatchProcessor);
}
传入的参数有:
-
ApplicationLike
应用的代理application
-
LoadReporter
加载合成的包的报告类 -
PatchReporter
打修复包过程中的报告类 -
PatchListener
对修复包最开始的检查 -
ResultService
从合成进程取合成结果 -
UpgradePatchProcessor
生成一个新的patch合成包 -
ReparePatchProcessor
修复上一次合成失败的修复包
下面看一下Tinker
中核心的install
方法
/**
* you must install tinker first!!
*
* @param intentResult
* @param serviceClass
* @param upgradePatch
* @param repairPatch
*/public void install(Intent intentResult, Class extends
AbstractResultService> serviceClass,
AbstractPatch upgradePatch, AbstractPatch repairPatch) {
sInstalled = true;
AbstractResultService.setResultServiceClass(serviceClass);
TinkerPatchService.setPatchProcessor(upgradePatch, repairPatch);
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!");
}}
- 设置自定义的
ResultService
- 设置自定义的
UpgradePatch
和ReparePatch
- 创建
TinkerLoadResult
调用parseTinkerResult(Context context, Intent intentResult)
解析上次合成之后的信息:花费时间,返回值等。 - 调用
LoaderReporter
的onLoadResult
方法,通知,加载结果。
分析到些结束,下一篇将分析一下具体的合成过程。