經(jīng)過(guò)TinkerInstaller.install 安裝之后泌枪,Tinker相關(guān)初始化工作也都做好了塑陵,萬(wàn)事俱備等待手動(dòng)執(zhí)行合成patch包垫桂。
一咙冗、合成patch包
主動(dòng)觸發(fā):
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), patch);//patchs是diff patch路徑
TinkerInstaller.java
public static void onReceiveUpgradePatch(Context context, String patchLocation) {
Tinker.with(context).getPatchListener().onPatchReceived(patchLocation);
}
DefaultPatchListener.java
public int onPatchReceived(String path) {
final File patchFile = new File(path);
final String patchMD5 = SharePatchFileUtil.getMD5(patchFile);
final int returnCode = patchCheck(path, patchMD5);
if (returnCode == ShareConstants.ERROR_PATCH_OK) {
runForgService();
//補(bǔ)丁校驗(yàn)成功孵滞,則啟動(dòng)服務(wù)來(lái)處理patch合成中捆,這個(gè)服務(wù)是單獨(dú)起了patch進(jìn)程的
TinkerPatchService.runPatchService(context, path);
} else {
Tinker.with(context).getLoadReporter().onLoadPatchListenerReceiveFail(new File(path), returnCode);
}
return returnCode;
}
TinkerPatchService.java
public static void runPatchService(final Context context, final String path) {
TinkerLog.i(TAG, "run patch service...");
Intent intent = new Intent(context, TinkerPatchService.class);
intent.putExtra(PATCH_PATH_EXTRA, path);
intent.putExtra(RESULT_CLASS_EXTRA, resultServiceClass.getName());
try {
context.startService(intent);//啟動(dòng)當(dāng)前服務(wù)
} catch (Throwable thr) {
TinkerLog.e(TAG, "run patch service fail, exception:" + thr);
}
}
@Override
protected void onHandleIntent(Intent intent) {
increasingPriority();//提升服務(wù)優(yōu)先級(jí)為前臺(tái),減少內(nèi)存緊張時(shí)候被lmk kill的風(fēng)險(xiǎn)
doApplyPatch(this, intent);//服務(wù)執(zhí)行
}
private static void doApplyPatch(Context context, Intent intent) {
// Since we may retry with IntentService, we should prevent
// racing here again.
if (!sIsPatchApplying.compareAndSet(false, true)) {
TinkerLog.w(TAG, "TinkerPatchService doApplyPatch is running by another runner.");
return;
}
Tinker tinker = Tinker.with(context);
tinker.getPatchReporter().onPatchServiceStart(intent);//初始化重試相關(guān)信息配置
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;
PatchResult patchResult = new PatchResult();
try {
if (upgradePatchProcessor == null) {
throw new TinkerRuntimeException("upgradePatchProcessor is null.");
}
//合成patch
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;
//通過(guò)AbstractResultService實(shí)現(xiàn)類來(lái)處理返回結(jié)果坊饶,比如定制一些合成完成之后的退出機(jī)制泄伪。
AbstractResultService.runResultService(context, patchResult, getPatchResultExtra(intent));
sIsPatchApplying.set(false);
}
合成patch看upgradePatchProcessor.tryPatch
UpgradePatch.java
@Override
public boolean tryPatch(Context context, String tempPatchPath, PatchResult patchResult) {
Tinker manager = Tinker.with(context);
final File patchFile = new File(tempPatchPath);
//tinker熱修復(fù)enbale
if (!manager.isTinkerEnabled() || !ShareTinkerInternals.isTinkerEnableWithSharedPreferences(context)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:patch is disabled, just return");
return false;
}
//patch文件合法
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();
//info.lock 文件
File patchInfoLockFile = SharePatchFileUtil.getPatchInfoLockFile(patchDirectory);
//patch.info 文件
File patchInfoFile = SharePatchFileUtil.getPatchInfoFile(patchDirectory);
final Map<String, String> pkgProps = signatureCheck.getPackagePropertiesIfPresent();
if (pkgProps == null) {
TinkerLog.e(TAG, "UpgradePatch packageProperties is null, do we process a valid patch apk ?");
return false;
}
final String isProtectedAppStr = pkgProps.get(ShareConstants.PKGMETA_KEY_IS_PROTECTED_APP);
final boolean isProtectedApp = (isProtectedAppStr != null && !isProtectedAppStr.isEmpty() && !"0".equals(isProtectedAppStr));
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;
}
final boolean usingInterpret = oldInfo.oatDir.equals(ShareConstants.INTERPRET_DEX_OPTIMIZE_PATH);
if (!usingInterpret && !ShareTinkerInternals.isNullOrNil(oldInfo.newVersion) && oldInfo.newVersion.equals(patchMd5) && !oldInfo.isRemoveNewVersion) {
TinkerLog.e(TAG, "patch already applied, md5: %s", patchMd5);
// Reset patch apply retry count to let us be able to reapply without triggering
// patch apply disable when we apply it successfully previously.
UpgradePatchRetry.getInstance(context).onPatchResetMaxCheck(patchMd5);
return true;
}
// if it is interpret now, use changing flag to wait main process
final String finalOatDir = usingInterpret ? ShareConstants.CHANING_DEX_OPTIMIZE_PATH : oldInfo.oatDir;
newInfo = new SharePatchInfo(oldInfo.oldVersion, patchMd5, isProtectedApp, false, Build.FINGERPRINT, finalOatDir, false);
} else {
newInfo = new SharePatchInfo("", patchMd5, isProtectedApp, false, Build.FINGERPRINT, ShareConstants.DEFAULT_DEX_OPTIMIZE_PATH, false);
}
// 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
//把補(bǔ)丁包復(fù)制到/data/data/com.stan.tinkersdkdemo/tinker/patch-8b79c8cc/patch-8b79c8cc.apk
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) {
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
//合成dex
if (!DexDiffPatchInternal.tryRecoverDexFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch dex failed");
return false;
}
//合成arkhot 針對(duì)方舟os
if (!ArkHotDiffPatchInternal.tryRecoverArkHotLibrary(manager, signatureCheck,
context, patchVersionDirectory, destPatchFile)) {
return false;
}
//合成so
if (!BsDiffPatchInternal.tryRecoverLibraryFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch library failed");
return false;
}
//合成resource
if (!ResDiffPatchInternal.tryRecoverResourceFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile)) {
TinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch resource failed");
return false;
}
//對(duì)dex進(jìn)行opt優(yōu)化
// 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;
}
//把結(jié)果重新寫入到 patch.info
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;
}
// Reset patch apply retry count to let us be able to reapply without triggering
// patch apply disable when we apply it successfully previously.
UpgradePatchRetry.getInstance(context).onPatchResetMaxCheck(patchMd5);
TinkerLog.w(TAG, "UpgradePatch tryPatch: done, it is ok");
return true;
}
這里很簡(jiǎn)單,就是把你存放的patch差分包復(fù)制到/data/data/com.stan.tinkersdkdemo/tinker/patch-8b79c8cc/patch-8b79c8cc.apk中匿级,然后分別執(zhí)行dex蟋滴、so 、resources與基準(zhǔn)包的合成痘绎。
dex脓杉、so 、resources的差分與合成有時(shí)候的話后續(xù)再單獨(dú)開(kāi)篇分析下简逮。
參考: