从加载流程来分析
处理下载之后的修复包:
/**
* 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
方法,通知,加载结果。
分析到些结束,下一篇将分析一下具体的合成过程。
网友评论