美文网首页
热修复框架 - Tinker disable逻辑梳理

热修复框架 - Tinker disable逻辑梳理

作者: Stan_Z | 来源:发表于2020-11-10 22:56 被阅读0次

    代码基于tinker 1.9.14.7

    一、tinker disable方式:

    Tinker.with(context).setTinkerDisable();//内存保存flag,禁止当前进程重复触发tinker热修复。

    ShareTinkerInternals.setTinkerDisableWithSharedPreferences(context)//sp持久化,禁止当前安装应用重复触发tinker热修复。

    二、tinker disable场景:

    2.1 Tinker.with(context).setTinkerDisable()
    加载异常

    DefaultLoadReporter.onLoadException 
    TinkerLoadResult.parseTinkerResult{
    ...
    //found uncaught exception, just return
    Throwable exception = ShareIntentUtil.getIntentPatchException(intentResult);
    if (exception != null) {
      ...
        tinker.getLoadReporter().onLoadException(exception, errorCode);
        return false;
    }
    ...
    }
    

    合成异常

    DefaultPatchReporter.onPatchException  
    TinkerPatchService.doApplyPatch{
    ...
    try {
        if (upgradePatchProcessor == null) {
            throw new TinkerRuntimeException("upgradePatchProcessor is null.");
        }
        result = upgradePatchProcessor.tryPatch(context, path, patchResult);
    } catch (Throwable throwable) {
        e = throwable;
        result = false;
        tinker.getPatchReporter().onPatchException(patchFile, e);
    }
    
    ...
    }
    

    2.2 ShareTinkerInternals.setTinkerDisableWithSharedPreferences

    DefaultLoadReporter onLoadException {
    
      switch (errorCode) {
         case ShareConstants.ERROR_LOAD_EXCEPTION_DEX: load dex失败
           ...
            ShareTinkerInternals.setTinkerDisableWithSharedPreferences(context);
            break;
        case ShareConstants.ERROR_LOAD_EXCEPTION_RESOURCE: load res失败
           ...
            ShareTinkerInternals.setTinkerDisableWithSharedPreferences(context);
            break;
        case ShareConstants.ERROR_LOAD_EXCEPTION_UNCAUGHT: 安全模式进入3次
           ...
            ShareTinkerInternals.setTinkerDisableWithSharedPreferences(context);
           ...
            break;
        case ShareConstants.ERROR_LOAD_EXCEPTION_UNKNOWN:反射获取tinker loader失败
           无任何逻辑
            break;
        default:
            break;
       }
    ...
    }
    

    三、tinker disable逻辑分析

    Tinker.with(context).setTinkerDisable()覆盖patch合成和加载两个场景,本次热修复不再重复,但是下次重启app又会触发热修复(前提是tinkerFlags 不为 TINKER_DISABLE)。

    ShareTinkerInternals.setTinkerDisableWithSharedPreferences(context)覆盖patch加载场景,针对加载dex、res以及安全模式验证这三处非客户端逻辑部分。此处触发的加载失败一般与系统相关,因此tinker在此处无差别进行了持久化级别的disable。

    四、案例

    这里主要分析下ShareTinkerInternals.setTinkerDisableWithSharedPreferences(context)场景:
    基于tinker 1.9.6的错误日志:

    2020-09-16 18:24:51.361 8791-8791/? E/Tinker.ResourceLoader: resource hook check failed.
    java.lang.NoSuchFieldException: Field mStringBlocks not found in class android.content.res.AssetManager
    at com.tencent.tinker.loader.shareutil.ShareReflectUtil.findField(ShareReflectUtil.java:55)
    at com.tencent.tinker.loader.TinkerResourcePatcher.a(TinkerResourcePatcher.java:101)
    at com.tencent.tinker.loader.TinkerResourceLoader.checkComplete(TinkerResourceLoader.java:122)
    at com.tencent.tinker.loader.TinkerLoader.tryLoadPatchFilesInternal(TinkerLoader.java:216)
    at com.tencent.tinker.loader.TinkerLoader.tryLoad(TinkerLoader.java:58)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.tencent.tinker.loader.app.TinkerApplication.loadTinker(TinkerApplication.java:163)
    at com.tencent.tinker.loader.app.TinkerApplication.onBaseContextAttached(TinkerApplication.java:132)
    at com.tencent.tinker.loader.app.TinkerApplication.attachBaseContext(TinkerApplication.java:148)
    

    这里TinkerResourceLoader.checkComplete 之后走的逻辑是:

    try {
        TinkerResourcePatcher.isResourceCanPatch(context);
    } catch (Throwable e) {
        Log.e(TAG, "resource hook check failed.", e);
        intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_EXCEPTION, e);
        ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_RESOURCE_LOAD_EXCEPTION);//返回res加载失败exception,设置sp为false
        return false;
    }
    

    报错点在TinkerResourcePatcher.isResourceCanPatch(context);

    Tinker 1.9.6:

    public static void isResourceCanPatch(Context context) throws Throwable {
      ...
        // Kitkat needs this method call, Lollipop doesn't. However, it doesn't seem to cause any harm
        // in L, so we do it unconditionally.
        stringBlocksField = findField(assets, "mStringBlocks");
        ensureStringBlocksMethod = findMethod(assets, "ensureStringBlocks");
        ...
    }
    

    Tinker 1.9.14.7:

    public static void isResourceCanPatch(Context context) throws Throwable {
        ...
        // Kitkat needs this method call, Lollipop doesn't. However, it doesn't seem to cause any harm
        // in L, so we do it unconditionally.
        try {
            stringBlocksField = findField(assets, "mStringBlocks");
            ensureStringBlocksMethod = findMethod(assets, "ensureStringBlocks");
        } catch (Throwable ignored) {
            // Ignored.
        }
       ...
    }
    

    1.9.6 反射android.content.res.AssetManager获取mStringBlocks属性找不到,也没try catch,直接抛异常。1.9.14.7进行了try catch...

    因为tinker做了大量的源码hook,容易出现异常,对这类问题tinker统一通过sp设置开关来无差别关闭热修复功能。但是这也会导致一个问题,就是例如res加载失败也会影响后续仅修改了dex的patch包的修复。
    因此这里建议对包进行区分,如果有新的修复包,还是打开开关,尝试做修复,仅对相同修复失败的包进行禁止。

    相关文章

      网友评论

          本文标题:热修复框架 - Tinker disable逻辑梳理

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