美文网首页Android
热修复框架 - TinkerApplication启动(一) -

热修复框架 - TinkerApplication启动(一) -

作者: Stan_Z | 来源:发表于2020-08-12 14:05 被阅读0次

    代码基于tinker 1.9.14.7。
    Tinker热修复从使用上来看主要有三方面:

    • TinkerApplication 启动过程相关
    • TinkerInstaller.installTinker 初始化相关
    • TinkerInstaller.onReceiveUpgradePatch 加载patch包

    这三点形成一个完整的热修复闭环,后续文章依次从这三个点铺开来分析,先对Tinker有个全面了解。本篇文章先从TinkerApplication 启动过程相关开始。

    一、动态生成Application

    接入 Tinker 官方推荐是利用 @DefaultLifeCycle 动态生成 Application。这里用到了apt在编译时生成类的方式。通过AnnotationProcessor 按resouces/TinkerAnnoApplication.tmpl模板生成。

    public class %APPLICATION% extends TinkerApplication {
        public %APPLICATION%() {
            super(%TINKER_FLAGS%, "%APPLICATION_LIFE_CYCLE%", "%TINKER_LOADER_CLASS%", %TINKER_LOAD_VERIFY_FLAG%);
        }
    }
    

    动态生成的application是继承于TinkerApplication。

    protected TinkerApplication(int tinkerFlags) {
        this(tinkerFlags, "com.tencent.tinker.entry.DefaultApplicationLike",
               TinkerLoader.class.getName(), false);
    }
    

    TinkerApplication初始化时关联DefaultApplicationLike,它作为生成的application的代理类供外部调用者使用。

    接下来再看

    private void onBaseContextAttached(Context base) {
        try {
            final long applicationStartElapsedTime = SystemClock.elapsedRealtime();
           final long applicationStartMillisTime = System.currentTimeMillis();
           loadTinker();
           mCurrentClassLoader = base.getClassLoader();
           mInlineFence = createInlineFence(this, tinkerFlags, delegateClassName,
                   tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime,
                   tinkerResultIntent);
           TinkerInlineFenceAction.callOnBaseContextAttached(mInlineFence, base);
           //reset save mode
           if (useSafeMode) {
                ShareTinkerInternals.setSafeModeCount(this, 0);
           }
        } catch (TinkerRuntimeException e) {
            throw e;
       } catch (Throwable thr) {
            throw new TinkerRuntimeException(thr.getMessage(), thr);
       }
    }
    

    在TinkerApplication的onBaseContextAttached中,主要做了两件事:

    • loadTinker: 反射执行TinkerLoader.tryLoad 方法。
    • createInlineFence,绑定代理DefaultApplicationLike。

    二、TinkerLoader.tryLoad 加载补丁包

    这里重点看看TinkerLoader.tryLoad,tryLoad直接调用tryLoadPatchFilesInternal,这个方法非常长,那么来耐心看下:

    private void tryLoadPatchFilesInternal(TinkerApplication app, Intent resultIntent) {
       final int tinkerFlag = app.getTinkerFlags();
       //确保tinker enable 且非patch进程
       if (!ShareTinkerInternals.isTinkerEnabled(tinkerFlag)) {
           Log.w(TAG, "tryLoadPatchFiles: tinker is disable, just return");
           ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_DISABLE);
           return;
       }
    
       if (ShareTinkerInternals.isInPatchProcess(app)) {
           Log.w(TAG, "tryLoadPatchFiles: we don't load patch with :patch process itself, just return");
           ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_DISABLE);
           return;
       }
    
       //tinker
       //PatchDirectory:/data/data/tinker.sample.android/tinker
       File patchDirectoryFile = SharePatchFileUtil.getPatchDirectory(app);
       if (patchDirectoryFile == null) {
           Log.w(TAG, "tryLoadPatchFiles:getPatchDirectory == null");
           //treat as not exist
           ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_DIRECTORY_NOT_EXIST);
           return;
       }
       String patchDirectoryPath = patchDirectoryFile.getAbsolutePath();
    
       // 检查tinker目录是否存在
       //check patch directory whether exist
       if (!patchDirectoryFile.exists()) {
           Log.w(TAG, "tryLoadPatchFiles:patch dir not exist:" + patchDirectoryPath);
           ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_DIRECTORY_NOT_EXIST);
           return;
       }
       //tinker/patch.info 补丁信息文件
       File patchInfoFile = SharePatchFileUtil.getPatchInfoFile(patchDirectoryPath);
       //check patch info file whether exist
       if (!patchInfoFile.exists()) {
           Log.w(TAG, "tryLoadPatchFiles:patch info not exist:" + patchInfoFile.getAbsolutePath());
           ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_INFO_NOT_EXIST);
           return;
       }
    
       //获取patch.info并包装为SharePatchInfo
       //old = 641e634c5b8f1649c75caf73794acbdf
       //new = 2c150d8560334966952678930ba67fa8
       File patchInfoLockFile = SharePatchFileUtil.getPatchInfoLockFile(patchDirectoryPath);
       patchInfo = SharePatchInfo.readAndCheckPropertyWithLock(patchInfoFile, patchInfoLockFile);
       if (patchInfo == null) {
           ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_INFO_CORRUPTED);
           return;
       }
       final boolean isProtectedApp = patchInfo.isProtectedApp;
       resultIntent.putExtra(ShareIntentUtil.INTENT_IS_PROTECTED_APP, isProtectedApp);
       String oldVersion = patchInfo.oldVersion;
       String newVersion = patchInfo.newVersion;
       String oatDex = patchInfo.oatDir;
       if (oldVersion == null || newVersion == null || oatDex == null) {
           //it is nice to clean patch
           Log.w(TAG, "tryLoadPatchFiles:onPatchInfoCorrupted");
           ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_INFO_CORRUPTED);
           return;
       }
       boolean mainProcess = ShareTinkerInternals.isInMainProcess(app);
       boolean isRemoveNewVersion = patchInfo.isRemoveNewVersion;
       //是否清除新patch
       if (mainProcess) {
           final String patchName = SharePatchFileUtil.getPatchVersionDirectory(newVersion);
           // So far new version is not loaded in main process and other processes.
           // We can remove new version directory safely.
           if (isRemoveNewVersion) {
               Log.w(TAG, "found clean patch mark and we are in main process, delete patch file now.");
               if (patchName != null) {
                   // oldVersion.equals(newVersion) means the new version has been loaded at least once
                   // after it was applied.
                   final boolean isNewVersionLoadedBefore = oldVersion.equals(newVersion);
                   if (isNewVersionLoadedBefore) {
                       // Set oldVersion and newVersion to empty string to clean patch
                       // if current patch has been loaded before.
                       oldVersion = "";
                   }
                   newVersion = oldVersion;
                   patchInfo.oldVersion = oldVersion;
                   patchInfo.newVersion = newVersion;
                   patchInfo.isRemoveNewVersion = false;
                   SharePatchInfo.rewritePatchInfoFileWithLock(patchInfoFile, patchInfo, patchInfoLockFile);
                   String patchVersionDirFullPath = patchDirectoryPath + "/" + patchName;
                   SharePatchFileUtil.deleteDir(patchVersionDirFullPath);
                   if (isNewVersionLoadedBefore) {
                       ShareTinkerInternals.killProcessExceptMain(app);
                       ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_DIRECTORY_NOT_EXIST);
                       return;
                   }
               }
           }
           if (patchInfo.isRemoveInterpretOATDir) {
               // delete interpret odex
               // for android o, directory change. Fortunately, we don't need to support android o interpret mode any more
               Log.i(TAG, "tryLoadPatchFiles: isRemoveInterpretOATDir is true, try to delete interpret optimize files");
               patchInfo.isRemoveInterpretOATDir = false;
               SharePatchInfo.rewritePatchInfoFileWithLock(patchInfoFile, patchInfo, patchInfoLockFile);
               ShareTinkerInternals.killProcessExceptMain(app);
               String patchVersionDirFullPath = patchDirectoryPath + "/" + patchName;
               SharePatchFileUtil.deleteDir(patchVersionDirFullPath + "/" + ShareConstants.INTERPRET_DEX_OPTIMIZE_PATH);
           }
       }
       resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_OLD_VERSION, oldVersion);
       resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_NEW_VERSION, newVersion);
       boolean versionChanged = !(oldVersion.equals(newVersion));
       boolean oatModeChanged = oatDex.equals(ShareConstants.CHANING_DEX_OPTIMIZE_PATH);
       oatDex = ShareTinkerInternals.getCurrentOatMode(app, oatDex);
       resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_OAT_DIR, oatDex);
       // 根据版本变化和是否是主进程的条件决定是否加载新patch
       String version = oldVersion;
       if (versionChanged && mainProcess) {
           version = newVersion;
       }
       if (ShareTinkerInternals.isNullOrNil(version)) {
           Log.w(TAG, "tryLoadPatchFiles:version is blank, wait main process to restart");
           ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_INFO_BLANK);
           return;
       }
       //patch-641e634c
       String patchName = SharePatchFileUtil.getPatchVersionDirectory(version);
       if (patchName == null) {
           Log.w(TAG, "tryLoadPatchFiles:patchName is null");
           //we may delete patch info file
           ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_VERSION_DIRECTORY_NOT_EXIST);
           return;
       }
       //tinker/patch.info/patch-641e634c
       String patchVersionDirectory = patchDirectoryPath + "/" + patchName;
       File patchVersionDirectoryFile = new File(patchVersionDirectory);
       if (!patchVersionDirectoryFile.exists()) {
           Log.w(TAG, "tryLoadPatchFiles:onPatchVersionDirectoryNotFound");
           //we may delete patch info file
           ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_VERSION_DIRECTORY_NOT_EXIST);
           return;
       }
       //tinker/patch.info/patch-641e634c/patch-641e634c.apk
       final String patchVersionFileRelPath = SharePatchFileUtil.getPatchVersionFile(version);
       //获取diff patch文件
       File patchVersionFile = (patchVersionFileRelPath != null ? new File(patchVersionDirectoryFile.getAbsolutePath(), patchVersionFileRelPath) : null);
       if (!SharePatchFileUtil.isLegalFile(patchVersionFile)) {
           Log.w(TAG, "tryLoadPatchFiles:onPatchVersionFileNotFound");
           //we may delete patch info file
           ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_VERSION_FILE_NOT_EXIST);
           return;
       }
       ShareSecurityCheck securityCheck = new ShareSecurityCheck(app);
       // 1. 检查补丁包 apk 的签名
       // 2. 检查基准包的 tinker id 与补丁包中是否一致
       // 3. 检查 tinker 设置与补丁包中的类型是否符合
    
       int returnCode = ShareTinkerInternals.checkTinkerPackage(app, tinkerFlag, patchVersionFile, securityCheck);
       if (returnCode != ShareConstants.ERROR_PACKAGE_CHECK_OK) {
           Log.w(TAG, "tryLoadPatchFiles:checkTinkerPackage");
           resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_PACKAGE_PATCH_CHECK, returnCode);
           ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_PACKAGE_CHECK_FAIL);
           return;
       }
       resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_PACKAGE_CONFIG, securityCheck.getPackagePropertiesIfPresent());
       final boolean isEnabledForDex = ShareTinkerInternals.isTinkerEnabledForDex(tinkerFlag);
       //com.huawei.ark.app.ArkApplicationInfo arthot是对华为的一种单独修复支持
       final boolean isArkHotRuning = ShareTinkerInternals.isArkHotRuning();
       if (!isArkHotRuning && isEnabledForDex) {
           //tinker/patch.info/patch-641e634c/dex
           boolean dexCheck = TinkerDexLoader.checkComplete(patchVersionDirectory, securityCheck, oatDex, resultIntent);
           if (!dexCheck) {
               //file not found, do not load patch
               Log.w(TAG, "tryLoadPatchFiles:dex check fail");
               return;
           }
       }
       final boolean isEnabledForArkHot = ShareTinkerInternals.isTinkerEnabledForArkHot(tinkerFlag);
       if (isArkHotRuning && isEnabledForArkHot) {
           boolean arkHotCheck = TinkerArkHotLoader.checkComplete(patchVersionDirectory, securityCheck, resultIntent);
           if (!arkHotCheck) {
               // file not found, do not load patch
               Log.w(TAG, "tryLoadPatchFiles:dex check fail");
               return;
           }
       }
       final boolean isEnabledForNativeLib = ShareTinkerInternals.isTinkerEnabledForNativeLib(tinkerFlag);
       if (isEnabledForNativeLib) {
           //tinker/patch.info/patch-641e634c/lib
           boolean libCheck = TinkerSoLoader.checkComplete(patchVersionDirectory, securityCheck, resultIntent);
           if (!libCheck) {
               //file not found, do not load patch
               Log.w(TAG, "tryLoadPatchFiles:native lib check fail");
               return;
           }
       }
       //check resource
       final boolean isEnabledForResource = ShareTinkerInternals.isTinkerEnabledForResource(tinkerFlag);
       Log.w(TAG, "tryLoadPatchFiles:isEnabledForResource:" + isEnabledForResource);
       if (isEnabledForResource) {
           boolean resourceCheck = TinkerResourceLoader.checkComplete(app, patchVersionDirectory, securityCheck, resultIntent);
           if (!resourceCheck) {
               //file not found, do not load patch
               Log.w(TAG, "tryLoadPatchFiles:resource check fail");
               return;
           }
       }
       //art环境且系统做ota升级,需要重新loadDexFile,因为之前的odex会失效。
       //only work for art platform oat,because of interpret, refuse 4.4 art oat
       //android o use quicken default, we don't need to use interpret mode
       boolean isSystemOTA = ShareTinkerInternals.isVmArt()
           && ShareTinkerInternals.isSystemOTA(patchInfo.fingerPrint)
           && Build.VERSION.SDK_INT >= 21 && !ShareTinkerInternals.isAfterAndroidO();
       resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_SYSTEM_OTA, isSystemOTA);
       //we should first try rewrite patch info file, if there is a error, we can't load jar
       if (mainProcess) {
           if (versionChanged) {
               patchInfo.oldVersion = version;
           }
           if (oatModeChanged) {
               patchInfo.oatDir = oatDex;
               patchInfo.isRemoveInterpretOATDir = true;
           }
       }
       if (!checkSafeModeCount(app)) {
           resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_EXCEPTION, new TinkerRuntimeException("checkSafeModeCount fail"));
           ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_UNCAUGHT_EXCEPTION);
           Log.w(TAG, "tryLoadPatchFiles:checkSafeModeCount fail");
           return;
       }
       //now we can load patch jar
       if (!isArkHotRuning && isEnabledForDex) {
           //加载dex补丁
           boolean loadTinkerJars = TinkerDexLoader.loadTinkerJars(app, patchVersionDirectory, oatDex, resultIntent, isSystemOTA, isProtectedApp);
           if (isSystemOTA) {
               // update fingerprint after load success
               patchInfo.fingerPrint = Build.FINGERPRINT;
               patchInfo.oatDir = loadTinkerJars ? ShareConstants.INTERPRET_DEX_OPTIMIZE_PATH : ShareConstants.DEFAULT_DEX_OPTIMIZE_PATH;
               // reset to false
               oatModeChanged = false;
               if (!SharePatchInfo.rewritePatchInfoFileWithLock(patchInfoFile, patchInfo, patchInfoLockFile)) {
                   ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_REWRITE_PATCH_INFO_FAIL);
                   Log.w(TAG, "tryLoadPatchFiles:onReWritePatchInfoCorrupted");
                   return;
               }
               // update oat dir
               resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_OAT_DIR, patchInfo.oatDir);
           }
           if (!loadTinkerJars) {
               Log.w(TAG, "tryLoadPatchFiles:onPatchLoadDexesFail");
               return;
           }
       }
    
       if (isArkHotRuning && isEnabledForArkHot) {
           boolean loadArkHotFixJars = TinkerArkHotLoader.loadTinkerArkHot(app, patchVersionDirectory, resultIntent);
           if (!loadArkHotFixJars) {
               Log.w(TAG, "tryLoadPatchFiles:onPatchLoadArkApkFail");
               return;
           }
       }
       //now we can load patch resource
       if (isEnabledForResource) {
           //加载资源补丁
           boolean loadTinkerResources = TinkerResourceLoader.loadTinkerResources(app, patchVersionDirectory, resultIntent);
           if (!loadTinkerResources) {
               Log.w(TAG, "tryLoadPatchFiles:onPatchLoadResourcesFail");
               return;
           }
       }
       // Init component hotplug support.
       if ((isEnabledForDex || isEnabledForArkHot) && isEnabledForResource) {
           ComponentHotplug.install(app, securityCheck);
       }
       if (!AppInfoChangedBlocker.tryStart(app)) {
           Log.w(TAG, "tryLoadPatchFiles:AppInfoChangedBlocker install fail.");
           ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_BAIL_HACK_FAILURE);
           return;
       }
       // Before successfully exit, we should update stored version info and kill other process
       // to make them load latest patch when we first applied newer one.
       if (mainProcess && (versionChanged || oatModeChanged)) {
           //update old version to new
           if (!SharePatchInfo.rewritePatchInfoFileWithLock(patchInfoFile, patchInfo, patchInfoLockFile)) {
               ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_REWRITE_PATCH_INFO_FAIL);
               Log.w(TAG, "tryLoadPatchFiles:onReWritePatchInfoCorrupted");
               return;
           }
           ShareTinkerInternals.killProcessExceptMain(app);
       }
       //all is ok!
       ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_OK);
       Log.i(TAG, "tryLoadPatchFiles: load end, ok!");
    }
    

    简单总结这个长方法的工作:

    • 1.一系列检查:tinker功能是否打开、tinker文件夹是否存在、patch.info文件是否存在。
    • 2.通过patch.info校验patch有效性,决定是否加载new patch。
    • 3.补丁包校验:签名检查、tinkerid是否与基准包一致等。
    • 4.检查tinkerFlag确认开启了哪些修复功能:
    application初始化时配置的tinkerFlag:
    ShareConstants.java
    public static final int TINKER_DISABLE             = 0x00;
    public static final int TINKER_DEX_MASK            = 0x01;
    public static final int TINKER_NATIVE_LIBRARY_MASK = 0x02;
    public static final int TINKER_RESOURCE_MASK       = 0x04;
    public static final int TINKER_ARKHOT_MASK = 0x08;
    public static final int TINKER_DEX_AND_LIBRARY     = TINKER_DEX_MASK | TINKER_NATIVE_LIBRARY_MASK | TINKER_ARKHOT_MASK;
    public static final int TINKER_ENABLE_ALL          = TINKER_DEX_MASK | TINKER_NATIVE_LIBRARY_MASK | TINKER_RESOURCE_MASK | TINKER_ARKHOT_MASK;
    
    • 5.加载dex、arthot、resource补丁。这里并没有尝试加载so,arthot是针对华为的,在1.9.6之后的某个版本专门针对华为新增的修复类型。

    6.art环境且系统做ota升级,需要重新loadDexFile,因为之前的odex会失效。

    另外,要强调的是:tinker文件夹是在执行合成的时候生成的,后续再分析,这里先看看文件夹内容:

    cepheus:/data/data/com.stan.tinkersdkdemo/tinker # ls -al
    total 36
    drwx------ 3 u0_a350 u0_a350 4096 2020-08-12 10:14 .
    drwx------ 8 u0_a350 u0_a350 4096 2020-08-12 10:14 ..
    -rw------- 1 u0_a350 u0_a350    0 2020-08-12 10:15 info.lock
    drwx------ 4 u0_a350 u0_a350 4096 2020-08-12 10:14 patch-8b79c8cc
    -rw------- 1 u0_a350 u0_a350  367 2020-08-12 10:15 patch.info // patch信息描述文件
    
    cepheus:/data/data/com.stan.tinkersdkdemo/tinker # cat patch.info
    #from old version:8b79c8cc9881601c1e53e20bbe320026 to new version:8b79c8cc9881601c1e53e20bbe320026
    #Wed Aug 12 10:15:25 GMT+08:00 2020
    old=8b79c8cc9881601c1e53e20bbe320026 //旧包md5
    is_remove_interpret_oat_dir=0
    print=Xiaomi/cepheus/cepheus\:10/QKQ1.190825.002/9.10.29\:user/release-keys
    dir=odex
    is_protected_app=0
    is_remove_new_version=0
    new=8b79c8cc9881601c1e53e20bbe320026 //新包md5
    
    cepheus:/data/data/com.stan.tinkersdkdemo/tinker/patch-8b79c8cc # ls -al
    total 40
    drwx------ 4 u0_a350 u0_a350 4096 2020-08-12 10:14 .
    drwx------ 3 u0_a350 u0_a350 4096 2020-08-12 10:14 ..
    drwx------ 3 u0_a350 u0_a350 4096 2020-08-12 10:14 dex
    drwx------ 2 u0_a350 u0_a350 4096 2020-08-12 10:14 odex
    -rw------- 1 u0_a350 u0_a350 3443 2020-08-12 10:14 patch-8b79c8cc.apk //合成前的diff patch包
    
    cepheus:/data/data/com.stan.tinkersdkdemo/tinker/patch-8b79c8cc/dex # ls -al
    total 2328
    drwx------ 3 u0_a350 u0_a350    4096 2020-08-12 10:14 .
    drwx------ 4 u0_a350 u0_a350    4096 2020-08-12 10:14 ..
    drwxrwx--x 3 u0_a350 u0_a350    4096 2020-08-12 10:14 oat
    -rw------- 1 u0_a350 u0_a350 2351677 2020-08-12 10:14 tinker_classN.apk //合成后的patch包
    

    再看看patch包构成:

    信息保存在assets中

    • package_meta.txt 补丁包的基本信息
    • dex_meta.txt dex补丁的信息
    • so_meta.txt so补丁的信息
    • res_meta.txt 资源补丁的信息

    package_meta.txt

    #base package config field
    #Wed Aug 12 10:13:45 CST 2020
    NEW_TINKER_ID=tinker_id_1.1
    TINKER_ID=tinker_id_1.1
    is_protected_app=0
    patchMessage=fix the 1.0 version's bugs
    patchVersion=1.0
    

    dex_meta.txt

    classes.dex,,4d97b3f8bcfdffb05e3ed5db5cbb8f83,4d97b3f8bcfdffb05e3ed5db5cbb8f83,35b4ca58525ecb64e20cd315d5832c70,3533914992,3549684149,jar
    test.dex,,56900442eb5b7e1de45449d0685e6e00,56900442eb5b7e1de45449d0685e6e00,0,0,0,jar
    

    TinkerApplication启动过程核心功能主要分两点:

    • 1通过@DefaultLifeCycle 利用apt动态生成 Application。这里tinker使用ApplicationLike作为Application的代理,它绑定了Application的生命周期,提供给客户端使用。
    • 2 TinkerLoader.tryLoad 尝试加载补丁包。

    这里加载补丁包类型:

    • dex
    • so
    • arkhot 针对华为方舟os
    • resource

    相关文章

      网友评论

        本文标题:热修复框架 - TinkerApplication启动(一) -

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