Tinker合并流程

1. TinkerInstaller # install()

TinkerInstaller主要提供了兩個(gè)install()方法魏铅,一個(gè)簡(jiǎn)單的袁翁,另一個(gè)復(fù)雜一點(diǎn)的適用于需要自定義更多功能的滋恬。

1-1 多參數(shù)的install()

先看這個(gè)多參數(shù)的install()蝌戒,主要:

  1. 使用傳入?yún)?shù)構(gòu)建了一個(gè)Tinker對(duì)象卵史,Tinker使用了構(gòu)建者模式案站。
  2. 調(diào)用了Tinker的create()和install()继准。
public static Tinker install(ApplicationLike applicationLike, LoadReporter loadReporter, PatchReporter patchReporter,
                           PatchListener listener, Class<? extends AbstractResultService> 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 單參數(shù)的install()

單參數(shù)的install()也是相同的枉证,只是構(gòu)建Tinker對(duì)象時(shí)沒有設(shè)置那些傳入的參數(shù)。

public static Tinker install(ApplicationLike applicationLike) {
    Tinker tinker = new Tinker.Builder(applicationLike.getApplication()).build();
    Tinker.create(tinker);
    tinker.install(applicationLike.getTinkerResultIntent());
    return tinker;
}

這里就能看出來TinkerInstaller是一個(gè)外觀模式移必,并沒有執(zhí)行初始化的工作室谚,真正工作的是Tinker類,所以我們來看Tinker避凝。

1-3 Tinker的單例模式

Tinker類是一個(gè)單例舞萄,用得是DCL,沒有處理DCL失效問題管削,可能是因?yàn)榘l(fā)生的概率太小了且處理會(huì)讓效率降低倒脓。最后是用Builder去構(gòu)造對(duì)象,使用了構(gòu)建者模式含思,Builder是Tinker內(nèi)部類崎弃,去管理Tinker的參數(shù)甘晤。

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的內(nèi)部,統(tǒng)一管理了一些參數(shù)饲做,包括多參數(shù)install()傳入的Reporter和Listener等线婚。

private final Context context;
private final boolean mainProcess;
private final boolean patchProcess;
private int status = -1;
private LoadReporter  loadReporter;
private PatchReporter patchReporter;
private PatchListener listener;
private File          patchDirectory;
private File          patchInfoFile;
private File          patchInfoLockFile;
private Boolean       tinkerLoadVerifyFlag;
1-4 Builder的構(gòu)造器

在構(gòu)造方法中會(huì)先初始化一些,主要是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()

之后就是構(gòu)建者模式塞弊,提供了一系列設(shè)置參數(shù)的方法。比如這個(gè)設(shè)置patchReporter的方法泪姨。設(shè)置的方法不要多次調(diào)用游沿,并不會(huì)覆蓋掉之前的設(shè)置,只會(huì)拋已經(jīng)設(shè)置過的異常肮砾。

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()中會(huì)判空诀黍,如果是之前還沒有初始賦值的參數(shù),就賦默認(rèn)值仗处。最后調(diào)用Tinker構(gòu)造器傳入初始化的參數(shù)創(chuàng)建Tinker對(duì)象并返回眯勾。

public Tinker build() {
    if (status == -1) {
        status = ShareConstants.TINKER_ENABLE_ALL;
    }
    // 如果調(diào)用的是但參數(shù)的install()就會(huì)在這里賦默認(rèn)值
    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對(duì)象初始化好后傳入Tinker的create()婆誓,來看create()吃环。這個(gè)方法就是賦值單例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在調(diào)用完create()后就會(huì)調(diào)用install()旷档,Tinker的install()也有兩個(gè)模叙,單參數(shù)的TinkerInstaller#install()調(diào)用單參數(shù)的install(),多參數(shù)的調(diào)用多參數(shù)的鞋屈,單參數(shù)也是調(diào)用多參數(shù)的install(),后面兩個(gè)參數(shù)就生成默認(rèn)對(duì)象傳入了故觅。

public void install(Intent intentResult) {
    install(intentResult, DefaultTinkerResultService.class, new UpgradePatch());
}

再看多參數(shù)的install()厂庇,它完成真正install的邏輯。主要工作:

  1. 置標(biāo)記sInstalled為true输吏。
  2. 將兩個(gè)參數(shù)注入到TinkerPatchService中权旷。
  3. 初始化TinkerLoadResult,調(diào)用onLoadResult()贯溅。
public void install(Intent intentResult, Class<? extends AbstractResultService> 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()

同樣因?yàn)橥庥^模式拄氯,所以TinkerInstaller沒有任何處理,直接交給Tinker對(duì)象中已經(jīng)初始化好的PatchListener的onPatchReceived()處理它浅。

public static void onReceiveUpgradePatch(Context context, String patchLocation) {
    Tinker.with(context).getPatchListener().onPatchReceived(patchLocation);
}
2-1 PatchListener # onPatchReceived()

PatchListener是一個(gè)接口译柏,只有一個(gè)onPatchReceived()接口方法。DefaultPatchListener是PatchListener實(shí)現(xiàn)類姐霍。

public interface PatchListener {
    int onPatchReceived(String path);
}
2-2 DefaultPatchListener # onPatchReceived()

可以自己去實(shí)現(xiàn)PatchListener鄙麦,但總歸需要處理的工作是相似的典唇。我們看一個(gè)默認(rèn)實(shí)現(xiàn),DefaultPatchListener胯府。在onPatchReceived()方法里:

  1. 調(diào)用patchCheck()對(duì)patch文件進(jìn)行一系列的安全性檢查介衔,去重寫這個(gè)方法也就可以實(shí)現(xiàn)自己的檢查邏輯了。
  2. 如果檢查是安全地就開啟TinkerPatchService骂因,開始合并補(bǔ)丁包炎咖。
  3. 如果檢查沒通過就調(diào)用LoadReporter的相關(guān)方法通知。
@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;
}

到這里中心就要轉(zhuǎn)去TinkerPatchService了寒波。

總結(jié)一下:

  1. TinkerInstaller使用了外觀模式乘盼,沒有真正邏輯處理,只是封裝了Tinker的各種調(diào)用影所,真正處理的邏輯在Tinker中蹦肴。
  2. Tinker使用了單例模式(DCL) + 構(gòu)建者模式。
  3. TinkerInstaller提供了兩個(gè)api:
    • install()用來創(chuàng)建并初始化Tinker對(duì)象猴娩,并調(diào)用了Tinker對(duì)象的create()和install()阴幌。
    • onReceiveUpgradePatch()中調(diào)用了Tinker對(duì)象中PatchListener的onPatchReceived()。
  4. Tinker的install()中也初始了TinkerPatchService卷中,為后面做準(zhǔn)備矛双。
  5. PatchListener的onPatchReceived()用來檢查patch文件合法性并開啟執(zhí)行修復(fù)工作的TinkerPatchService。

3. TinkerPatchService

TinkerPatchService就是加載合并patch文件的Service蟆豫,繼承了IntentService议忽。

3-1 TinkerPatchService # setPatchProcessor()

回顧一下Tinker的install(),在install()中就傳入了兩個(gè)參數(shù)調(diào)用了setPatchProcessor()十减。再追溯參數(shù)來源其實(shí)是在我們自己編寫代碼調(diào)用TinkerInstaller的復(fù)雜install()傳入的或是調(diào)用簡(jiǎn)單install()時(shí)Tinker的Builder為我們默認(rèn)創(chuàng)建的栈幸。

多參數(shù)的源頭:

// 回顧我們自己編寫的代碼,調(diào)用 TinkerInstaller#install()帮辟。
// 最終最后兩個(gè)參數(shù)被注入到TinkerPatchService中速址。
AbstractPatch upgradePatchProcessor = new UpgradePatch();
TinkerInstaller.install(mAppLike, loadReporter, 
        patchReporter, mPatchListener,
        CustomResultService.class,// 決定在patch文件安裝完畢后的操作
        upgradePatchProcessor// 決定patch文件的安裝策略
);// 復(fù)雜的初始化方法

單參數(shù)的源頭:

// Tinker # install()
public void install(Intent intentResult) {
    // 傳入的是為我們默認(rèn)實(shí)例的對(duì)象。
    install(intentResult, DefaultTinkerResultService.class, new UpgradePatch());
}

一直傳遞到setPatchProcessor()由驹,最后向TinkerPatchService注入了這兩個(gè)對(duì)象芍锚。

  • upgradePatch是我們直接創(chuàng)建的UpgradePatch()對(duì)象,表示patch文件的安裝策略蔓榄。
  • serviceClass是我們自定義的DefaultTinkerResultService類并炮,表示修復(fù)完畢后的動(dòng)作。
public static void setPatchProcessor(AbstractPatch upgradePatch, Class<? extends AbstractResultService> serviceClass) {
    upgradePatchProcessor = upgradePatch;
    resultServiceClass = serviceClass;
    //try to load
    try {
        Class.forName(serviceClass.getName());
    } catch (ClassNotFoundException e) {
          e.printStackTrace();
    }
}
3-2 TinkerPatchService # runPatchService()

接著上面看一下runPatchService()甥郑,就是啟動(dòng)TinkerPatchService的代碼逃魄,intent中還傳了一個(gè)patch文件路徑path和一個(gè)在Tinker#install()中傳入的一個(gè)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. 在這個(gè)方法中最重要的就是調(diào)用了tryPatch()壹若,也就是更近一步的修復(fù)邏輯嗅钻,因?yàn)閛nHandleIntent()是支持耗時(shí)操作的皂冰,所以完全可以猜想tryPatch()是同步方法。
  2. 其次重要就是在處理完tryPatch()之后會(huì)開啟ResultService养篓,執(zhí)行修復(fù)完畢后的工作秃流。
@Override
protected void onHandleIntent(Intent intent) {
    final Context context = getApplicationContext();
    Tinker tinker = Tinker.with(context);
    // PatchReporter回調(diào)
    tinker.getPatchReporter().onPatchServiceStart(intent);
    
    // ...一些異常判斷處理...
    
    // 提升進(jìn)程優(yōu)先級(jí),盡可能保證此Service不被kill
    increasingPriority();
    PatchResult patchResult = new PatchResult();
    try {
        if (upgradePatchProcessor == null) {
            throw new TinkerRuntimeException("upgradePatchProcessor is null.");
        }
        // 核心調(diào)用tryPatch()
        result = upgradePatchProcessor.tryPatch(context, path, patchResult);
    } catch (Throwable throwable) {
        e = throwable;
        result = false;
        // PatchReporter回調(diào)
        tinker.getPatchReporter().onPatchException(patchFile, e);
    }
    
    cost = SystemClock.elapsedRealtime() - begin;
    // PatchReporter回調(diào)
    tinker.getPatchReporter().onPatchResult(patchFile, result, cost);
    
    patchResult.isSuccess = result;
    patchResult.rawPatchFilePath = path;
    patchResult.costTime = cost;
    patchResult.e = e;
    
    // 開始執(zhí)行ResultService柳弄,執(zhí)行修復(fù)完畢后的工作
    AbstractResultService.runResultService(context, patchResult, getPatchResultExtra(intent));
}
3-4 TinkerPatchService # increasingPriority()

在說兩個(gè)核心過程之前先來看這個(gè)方法舶胀,這個(gè)方法在tryPatch()之前被調(diào)用,主要是利用系統(tǒng)漏洞讓Service優(yōu)先級(jí)高一些避免輕易被回收碧注。

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類的嚣伐,只有一個(gè)抽象方法tryPatch()。

public abstract class AbstractPatch {

    public abstract boolean tryPatch(Context context, String tempPatchPath, PatchResult patchResult);
}

upgradePatchProcessor是AbstractPatch的實(shí)例對(duì)象萍丐,在Tinker的install()調(diào)用時(shí)傳入轩端,也就是多參數(shù)TinkerInstaller的install()傳入的,實(shí)現(xiàn)類是UpgradePatch逝变,看tryPatch()實(shí)現(xiàn)基茵,真的是非常長(zhǎng),我們分成兩個(gè)部分來看壳影,顯示檢查再是算法調(diào)用拱层。

4-2 UpgradePatch # tryPatch()中的檢查邏輯

tryPatch()的返回值便是合成Patch成功與否,在方法的開始都是一些判斷宴咧,對(duì)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參數(shù)和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
    // 構(gòu)建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不空且長(zhǎng)度正確
        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正確后拷貝文件宿饱,因?yàn)楹竺娌僮骺赡軙?huì)發(fā)生以外而刪除patch文件。
            // 所以在這里拷貝一份脚祟,后面的操作對(duì)拷貝的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;
    }
    
    // ......檢查成功的后序合并算法調(diào)用

    
}
4-3 UpgradePatch # tryPatch()中合并算法的調(diào)用

在通過了這一系列檢查之后就到了真正合并文件算法的時(shí)候了,合并的文件分為三種:dex文件由桌、.so文件和資源文件分別對(duì)應(yīng)下面三個(gè)調(diào)用为黎,只要一個(gè)修復(fù)工作失敗了邮丰,就返回false,修復(fù)算法我們?cè)谙乱黄治觥?/p>

@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()調(diào)用完成后剪廉,最后一句:

AbstractResultService.runResultService(context, patchResult, getPatchResultExtra(intent));

開啟了修復(fù)完成后的工作Service,DefaultTinkerResultService是默認(rèn)實(shí)現(xiàn)炕檩,或者也可以自定義斗蒋,在TinkerInstaller#install()傳入。

5-1 AbstractResultService # runResultService()

resultServiceClass是一路傳遞過來的類名笛质,到這里就是啟動(dòng)了泉沾,默認(rèn)給的是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沒有重寫該方法,父類實(shí)現(xiàn)直接調(diào)用的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()

首先會(huì)關(guān)閉PatchService妇押,然后刪除patch文件跷究,最后將應(yīng)用進(jìn)程殺死,再開就生效了敲霍。但是這樣的體驗(yàn)不好俊马,所以如果想要自己的邏輯,就可以自定義DefaultTinkerResultService色冀,重寫onPatchService()潭袱。

@Override
public void onPatchResult(PatchResult result) {
    
    // ......一些判斷和日志打印
    
    //first, we want to kill the recover process
    // 關(guān)閉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) {
        // 如果修復(fù)成功了,就把patch刪掉
        deleteRawPatchFile(new File(result.rawPatchFilePath));
        if (checkIfNeedKill(result)) {
            // 這就是為什么不自定義ResultService時(shí)锋恬,修復(fù)完成應(yīng)用會(huì)閃退
            android.os.Process.killProcess(android.os.Process.myPid());
        } else {
            TinkerLog.i(TAG, "I have already install the newly patch version!");
        }
    }
}

到這里整個(gè)流程就結(jié)束了屯换,默認(rèn)的話此時(shí)進(jìn)程已被殺死,再次啟動(dòng)才能夠生效与学。就再分析分析啟動(dòng)過程中發(fā)生的事情彤悔。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市索守,隨后出現(xiàn)的幾起案子晕窑,更是在濱河造成了極大的恐慌,老刑警劉巖卵佛,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杨赤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡截汪,警方通過查閱死者的電腦和手機(jī)疾牲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衙解,“玉大人阳柔,你說我怎么就攤上這事◎韭停” “怎么了舌剂?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵济锄,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我霍转,道長(zhǎng)荐绝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任谴忧,我火速辦了婚禮很泊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘沾谓。我一直安慰自己委造,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布均驶。 她就那樣靜靜地躺著昏兆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪妇穴。 梳的紋絲不亂的頭發(fā)上爬虱,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音腾它,去河邊找鬼跑筝。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瞒滴,可吹牛的內(nèi)容都是我干的曲梗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼妓忍,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼虏两!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起世剖,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤定罢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后旁瘫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祖凫,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年酬凳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蝙场。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡粱年,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出罚拟,到底是詐尸還是另有隱情台诗,我是刑警寧澤完箩,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站拉队,受9級(jí)特大地震影響弊知,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜粱快,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一秩彤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧事哭,春花似錦漫雷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谤辜,卻和暖如春蓄坏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背丑念。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工涡戳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人脯倚。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓渔彰,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親挠将。 傳聞我的和親對(duì)象是個(gè)殘疾皇子胳岂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容