美文网首页
热修复框架 - 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