美文网首页
Tinker源码解析

Tinker源码解析

作者: 小楠总 | 来源:发表于2017-12-01 21:34 被阅读237次

    源码分析入口-TinkerInstaller.install

    分析源码最好从调用的入口进行入手分析,因此我们从TinkerInstaller.install入手:

    //TinkerInstaller.install(mApplicationLike);
    TinkerInstaller.install(
            mApplicationLike,
            new DefaultLoadReporter(getApplication()),
            new DefaultPatchReporter(getApplication()),
            sCustomPatchListener,
            CustomResultService.class,
            new UpgradePatch()
    );
    

    TinkerInstaller的设计应用到了外观模式,相关的核心逻辑方法都由Tinker类来完成:

    public class TinkerInstaller {
        private static final String TAG = "Tinker.TinkerInstaller";
    
        public static Tinker install(ApplicationLike applicationLike) {
            Tinker tinker = new Tinker.Builder(applicationLike.getApplication()).build();
            Tinker.create(tinker);
            tinker.install(applicationLike.getTinkerResultIntent());
            return tinker;
        }
    
        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;
        }
    
        public static void cleanPatch(Context context) {
            Tinker.with(context).cleanPatch();
        }
    
        public static void onReceiveUpgradePatch(Context context, String patchLocation) {
            Tinker.with(context).getPatchListener().onPatchReceived(patchLocation);
        }
    
        public static void setLogIml(TinkerLog.TinkerLogImp imp) {
            TinkerLog.setTinkerLogImp(imp);
        }
    }
    

    因此,我们的关注点转移到Tinker类中,下面是省略后的Tinker代码,可以明确得看到:Tinker是一个单利,这里也用到了Builder模式来构建Tinker的单利:

    public class Tinker {
        private static final String TAG = "Tinker.Tinker";
    
        private static Tinker sInstance;
        private static boolean sInstalled = false;
    
        final Context       context;
        final File          patchDirectory;
        final PatchListener listener;
        final LoadReporter  loadReporter;
        final PatchReporter patchReporter;
        final File          patchInfoFile;
        final File          patchInfoLockFile;
        final boolean       isMainProcess;
        final boolean       isPatchProcess;
        final boolean       tinkerLoadVerifyFlag;
        int              tinkerFlags;
        TinkerLoadResult tinkerLoadResult;
        private boolean loaded = false;
    
        private Tinker(Context context, int tinkerFlags, LoadReporter loadReporter, PatchReporter patchReporter,
                       PatchListener listener, File patchDirectory, File patchInfoFile, File patchInfoLockFile,
                       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.patchInfoLockFile = patchInfoLockFile;
            this.isMainProcess = isInMainProc;
            this.tinkerLoadVerifyFlag = tinkerLoadVerifyFlag;
            this.isPatchProcess = isPatchProcess;
        }
    
        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;
        }
    
        public static void create(Tinker tinker) {
            if (sInstance != null) {
                throw new TinkerRuntimeException("Tinker instance is already set.");
            }
            sInstance = tinker;
        }
    
        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!");
            }
        }
    
        public static class Builder {
            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;
    
            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);
            }
            //省略一部分代码
        }
    
    }
    ```
    
    很简单地,通过with方法就可以获取一个Tinker的实例。
    
    ###源码分析入口-TinkerInstaller.onReceiveUpgradePatch
    
    另外一个入口就是我们加载Patch的时候调用到了TinkerInstaller.onReceiveUpgradePatch:
    
    ```java
    TinkerInstaller.onReceiveUpgradePatch(getApplication(), path);
    ```
    
    也就是调用:
    
    ```java
    public static void onReceiveUpgradePatch(Context context, String patchLocation) {
        Tinker.with(context).getPatchListener().onPatchReceived(patchLocation);
    }
    ```
    
    这里先获取了Tinker实例,然后获取PatchListener,最终调用PatchListener的onPatchReceived方法。其中PatchListener是一个接口,默认的实现是DefaultPatchListener:
    
    ```java
    public class DefaultPatchListener implements PatchListener {
        protected final Context context;
    
        public DefaultPatchListener(Context context) {
            this.context = context;
        }
    
        @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;
        }
    
        protected int patchCheck(String path, String patchMd5) {
            Tinker manager = Tinker.with(context);
            //check SharePreferences also
            if (!manager.isTinkerEnabled() || !ShareTinkerInternals.isTinkerEnableWithSharedPreferences(context)) {
                return ShareConstants.ERROR_PATCH_DISABLE;
            }
            File file = new File(path);
    
            if (!SharePatchFileUtil.isLegalFile(file)) {
                return ShareConstants.ERROR_PATCH_NOTEXIST;
            }
    
            //patch service can not send request
            if (manager.isPatchProcess()) {
                return ShareConstants.ERROR_PATCH_INSERVICE;
            }
    
            //if the patch service is running, pending
            if (TinkerServiceInternals.isTinkerPatchServiceRunning(context)) {
                return ShareConstants.ERROR_PATCH_RUNNING;
            }
            if (ShareTinkerInternals.isVmJit()) {
                return ShareConstants.ERROR_PATCH_JIT;
            }
    
            Tinker tinker = Tinker.with(context);
    
            if (tinker.isTinkerLoaded()) {
                TinkerLoadResult tinkerLoadResult = tinker.getTinkerLoadResultIfPresent();
                if (tinkerLoadResult != null && !tinkerLoadResult.useInterpretMode) {
                    String currentVersion = tinkerLoadResult.currentVersion;
                    if (patchMd5.equals(currentVersion)) {
                        return ShareConstants.ERROR_PATCH_ALREADY_APPLY;
                    }
                }
            }
    
            if (!UpgradePatchRetry.getInstance(context).onPatchListenerCheck(patchMd5)) {
                return ShareConstants.ERROR_PATCH_RETRY_COUNT_LIMIT;
            }
    
            return ShareConstants.ERROR_PATCH_OK;
        }
    }
    ```
    
    分析onPatchReceived方法可以知道最核心的是调用了TinkerPatchService的runPatchService:
    
    ```java
    TinkerPatchService.runPatchService(context, path);
    ```
    
    TinkerPatchService的runPatchService方法完成了Patch的加载以及合并。TinkerPatchService的具体实现如下:
    
    ```java
    public class TinkerPatchService extends IntentService {
    
        public TinkerPatchService() {
            super(com.tencent.tinker.lib.service.TinkerPatchService.class.getSimpleName());
        }
    
        //开启TinkerPatchService
        public static void runPatchService(Context context, String path) {
            try {
                Intent intent = new Intent(context, com.tencent.tinker.lib.service.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);
            }
        }
    
        //设置upgradePatch
        public static void setPatchProcessor(AbstractPatch upgradePatch, Class<? extends AbstractResultService> serviceClass) {
            upgradePatchProcessor = upgradePatch;
            resultServiceClass = serviceClass;
            try {
                Class.forName(serviceClass.getName());
            } catch (ClassNotFoundException e) {
            }
        }
    
        //最核心的加载与合并Patch的逻辑
        @Override
        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.");
                }
    
                //Patch合并的逻辑都在tryPatch里面
                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;
    
            //处理结果,这里可以进行扩展,例如防止应用重启
            AbstractResultService.runResultService(context, patchResult, getPatchResultExtra(intent));
        }
        
    }
    ```
    
    TinkerPatchService里面有几个很重要的方法,笔者都分别加上注释了。最核心的加载与合并Patch的逻辑onHandleIntent里面的AbstractPatch.tryPatch方法。
    
    而AbstractPatch的具体默认实现是UpgradePatch(从前面的install方法可以看出来),下面将进入Tinker的核心逻辑,也就是UpgradePatch的tryPatch方法:
    
    ```java
    public class UpgradePatch extends AbstractPatch {
        private static final String TAG = "Tinker.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 (!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();
    
            File patchInfoLockFile = SharePatchFileUtil.getPatchInfoLockFile(patchDirectory);
            File patchInfoFile = SharePatchFileUtil.getPatchInfoFile(patchDirectory);
    
            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;
                }
                // 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);
            }
    
            //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
            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) {
    //            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;
            }
    
            //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;
            }
    
            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;
            }
    
            TinkerLog.w(TAG, "UpgradePatch tryPatch: done, it is ok");
            return true;
        }
    
    }
    ```
    
    ###Tinker核心逻辑
    
    前面都是一些MD5、签名的校验,最核心的是:
    
    ```java
    //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;
    }
    
    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;
    }
    ```
    
    这里分别调用不同的Diff算法,处理了Dex(代码)、so、资源等。这里以Dex文件的处理为例进行讲解,一路分析tryRecoverDexFiles,最终会发现是调用了DexPatchApplier的executeAndSaveTo方法:
    
    ```java
    public void executeAndSaveTo(OutputStream out) throws IOException {
        // Before executing, we should check if this patch can be applied to
        // old dex we passed in.
        byte[] oldDexSign = this.oldDex.computeSignature(false);
        if (oldDexSign == null) {
            throw new IOException("failed to compute old dex's signature.");
        }
        if (this.patchFile == null) {
            throw new IllegalArgumentException("patch file is null.");
        }
        byte[] oldDexSignInPatchFile = this.patchFile.getOldDexSignature();
        if (CompareUtils.uArrCompare(oldDexSign, oldDexSignInPatchFile) != 0) {
            throw new IOException(
                    String.format(
                            "old dex signature mismatch! expected: %s, actual: %s",
                            Arrays.toString(oldDexSign),
                            Arrays.toString(oldDexSignInPatchFile)
                    )
            );
        }
    
        // Firstly, set sections' offset after patched, sort according to their offset so that
        // the dex lib of aosp can calculate section size.
        TableOfContents patchedToc = this.patchedDex.getTableOfContents();
    
        patchedToc.header.off = 0;
        patchedToc.header.size = 1;
        patchedToc.mapList.size = 1;
    
        //获取Patch文件中的Dex文件的分区以及子分区的相关偏移量
    
        patchedToc.stringIds.off
                = this.patchFile.getPatchedStringIdSectionOffset();
        patchedToc.typeIds.off
                = this.patchFile.getPatchedTypeIdSectionOffset();
        patchedToc.typeLists.off
                = this.patchFile.getPatchedTypeListSectionOffset();
        patchedToc.protoIds.off
                = this.patchFile.getPatchedProtoIdSectionOffset();
        patchedToc.fieldIds.off
                = this.patchFile.getPatchedFieldIdSectionOffset();
        patchedToc.methodIds.off
                = this.patchFile.getPatchedMethodIdSectionOffset();
        patchedToc.classDefs.off
                = this.patchFile.getPatchedClassDefSectionOffset();
        patchedToc.mapList.off
                = this.patchFile.getPatchedMapListSectionOffset();
        patchedToc.stringDatas.off
                = this.patchFile.getPatchedStringDataSectionOffset();
        patchedToc.annotations.off
                = this.patchFile.getPatchedAnnotationSectionOffset();
        patchedToc.annotationSets.off
                = this.patchFile.getPatchedAnnotationSetSectionOffset();
        patchedToc.annotationSetRefLists.off
                = this.patchFile.getPatchedAnnotationSetRefListSectionOffset();
        patchedToc.annotationsDirectories.off
                = this.patchFile.getPatchedAnnotationsDirectorySectionOffset();
        patchedToc.encodedArrays.off
                = this.patchFile.getPatchedEncodedArraySectionOffset();
        patchedToc.debugInfos.off
                = this.patchFile.getPatchedDebugInfoSectionOffset();
        patchedToc.codes.off
                = this.patchFile.getPatchedCodeSectionOffset();
        patchedToc.classDatas.off
                = this.patchFile.getPatchedClassDataSectionOffset();
        patchedToc.fileSize
                = this.patchFile.getPatchedDexSize();
    
        Arrays.sort(patchedToc.sections);
    
        patchedToc.computeSizesFromOffsets();
    
        //获取存在BUG的Dex文件的分区以及子分区的相关偏移量
    
        // Secondly, run patch algorithms according to sections' dependencies.
        this.stringDataSectionPatchAlg = new StringDataSectionPatchAlgorithm(
                patchFile, oldDex, patchedDex, oldToPatchedIndexMap
        );
        this.typeIdSectionPatchAlg = new TypeIdSectionPatchAlgorithm(
                patchFile, oldDex, patchedDex, oldToPatchedIndexMap
        );
        this.protoIdSectionPatchAlg = new ProtoIdSectionPatchAlgorithm(
                patchFile, oldDex, patchedDex, oldToPatchedIndexMap
        );
        this.fieldIdSectionPatchAlg = new FieldIdSectionPatchAlgorithm(
                patchFile, oldDex, patchedDex, oldToPatchedIndexMap
        );
        this.methodIdSectionPatchAlg = new MethodIdSectionPatchAlgorithm(
                patchFile, oldDex, patchedDex, oldToPatchedIndexMap
        );
        this.classDefSectionPatchAlg = new ClassDefSectionPatchAlgorithm(
                patchFile, oldDex, patchedDex, oldToPatchedIndexMap
        );
        this.typeListSectionPatchAlg = new TypeListSectionPatchAlgorithm(
                patchFile, oldDex, patchedDex, oldToPatchedIndexMap
        );
        this.annotationSetRefListSectionPatchAlg = new AnnotationSetRefListSectionPatchAlgorithm(
                patchFile, oldDex, patchedDex, oldToPatchedIndexMap
        );
        this.annotationSetSectionPatchAlg = new AnnotationSetSectionPatchAlgorithm(
                patchFile, oldDex, patchedDex, oldToPatchedIndexMap
        );
        this.classDataSectionPatchAlg = new ClassDataSectionPatchAlgorithm(
                patchFile, oldDex, patchedDex, oldToPatchedIndexMap
        );
        this.codeSectionPatchAlg = new CodeSectionPatchAlgorithm(
                patchFile, oldDex, patchedDex, oldToPatchedIndexMap
        );
        this.debugInfoSectionPatchAlg = new DebugInfoItemSectionPatchAlgorithm(
                patchFile, oldDex, patchedDex, oldToPatchedIndexMap
        );
        this.annotationSectionPatchAlg = new AnnotationSectionPatchAlgorithm(
                patchFile, oldDex, patchedDex, oldToPatchedIndexMap
        );
        this.encodedArraySectionPatchAlg = new StaticValueSectionPatchAlgorithm(
                patchFile, oldDex, patchedDex, oldToPatchedIndexMap
        );
        this.annotationsDirectorySectionPatchAlg = new AnnotationsDirectorySectionPatchAlgorithm(
                patchFile, oldDex, patchedDex, oldToPatchedIndexMap
        );
    
        //将Patch文件中的Dex文件的分区以及子分区的相关偏移量应用到有BUG的Dex文件中
    
        this.stringDataSectionPatchAlg.execute();
        this.typeIdSectionPatchAlg.execute();
        this.typeListSectionPatchAlg.execute();
        this.protoIdSectionPatchAlg.execute();
        this.fieldIdSectionPatchAlg.execute();
        this.methodIdSectionPatchAlg.execute();
        this.annotationSectionPatchAlg.execute();
        this.annotationSetSectionPatchAlg.execute();
        this.annotationSetRefListSectionPatchAlg.execute();
        this.annotationsDirectorySectionPatchAlg.execute();
        this.debugInfoSectionPatchAlg.execute();
        this.codeSectionPatchAlg.execute();
        this.classDataSectionPatchAlg.execute();
        this.encodedArraySectionPatchAlg.execute();
        this.classDefSectionPatchAlg.execute();
    
        // Thirdly, write header, mapList. Calculate and write patched dex's sign and checksum.
        Dex.Section headerOut = this.patchedDex.openSection(patchedToc.header.off);
        patchedToc.writeHeader(headerOut);
    
        Dex.Section mapListOut = this.patchedDex.openSection(patchedToc.mapList.off);
        patchedToc.writeMap(mapListOut);
    
        this.patchedDex.writeHashes();
    
        // Finally, write patched dex to file.
        this.patchedDex.writeTo(out);
    }
    ```
    
    这里干了3个核心逻辑:
    
    1. 获取Patch文件中的Dex文件的分区以及子分区的相关偏移量
    2. 获取存在BUG的Dex文件的分区以及子分区的相关偏移量
    3. 将Patch文件中的Dex文件的分区以及子分区的相关偏移量通过不同的算法应用到有BUG的Dex文件中
    
    其余的so、资源文件的处理都是分别通过不同Diff算法进行处理的,这里就不再赘述了,原理都差不多。
    
    ###结语
    
    到此为止,Tinker的核心流程就分析完毕了。在这次源码分析中,我们学会了Tinker是如何利用单例模式、Builder模式等设计模式进行设计,然后也学习到了不同类之间是如何分工合作的。当然最重要的是我们掌握了Tinker最核心的Diff算法。

    相关文章

      网友评论

          本文标题:Tinker源码解析

          本文链接:https://www.haomeiwen.com/subject/haoibxtx.html