Tinker源码解析系列—补丁合成与加载

引言 

上次我们分析了Tinker中关于Application的代理机制,这次我们来分析一下Tinker是如何进行补丁的合成与加载的。
上一篇我们讲了 Tinker源码解析系列—Application代理机制
阅读本文可能需要花费10分钟。

以下所有对源码的分析均基于Tinker 1.7.7 版本,且由于源码过长,均只贴出关键部分

Tinker中比较关键的几个类

在开始分析补丁合成和加载过程中,我们先来看看在Tinker中比较重要的几个类。

  • PatchListener Patch监听者,负责执行获取到补丁包后的后续操作
  • LoadReporter Load记录者,负责记录加载补丁包过程的状态信息
  • PatchReporter Patch记录者,负责记录合成补丁过程的状态信息
  • AbstractPatch Patch执行者,负责执行补丁的合成
    其中,PatchListener,LoadReporter,PatchReporter均为接口,AbstractPatch为抽象类,在Tinker中他们都有默认的实现类,对应关系如下:
  • PatchListener 默认实现类为DefaultPatchListener
  • LoadReporter 默认实现类为DefaultLoadReporter
  • PatchReporter 默认实现类为DefaultPatchReporter
  • AbstractPatch 默认实现类为UpgradePatch

Tinker的成员变量中包含了PatchListener, LoadReporter,PatchReporter

public class Tinker {
    ...
    final PatchListener listener;
    final LoadReporter  loadReporter;
    final PatchReporter patchReporter;
    ...
}

在Tinker中采用了Builder模式进行构造,我们直接看一下源码是怎么对他们进行赋值的

public static class Builder {
        public Tinker build() {
            ...
            if (loadReporter == null) {
                loadReporter = new DefaultLoadReporter(context);
            }

            if (patchReporter == null) {
                patchReporter = new DefaultPatchReporter(context);
            }

            if (listener == null) {
                listener = new DefaultPatchListener(context);
            }
            ...
            return new Tinker(context, status, loadReporter, patchReporter, listener, patchDirectory,
                patchInfoFile, patchInfoLockFile, mainProcess, patchProcess, tinkerLoadVerifyFlag);
        }
}

看在这里,为什么我们没有发现AbstractPatch 的影子呀?其实它是在install方法中进行设置的,我们直接看一下源码,

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

所以说Tinker提供了强大的自定义能力,我们完全能够去继承这几个类从而实现我们一些自定义的需求,好了不扯远了,这里我们大概清楚了这几个类的作用,接下来我们就进入正题

补丁的合成

在Tinker的官方Demo中,补丁合成调用的方法是

TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk");

我们点击该方法进去,

public class TinkerInstaller { 
   public static void onReceiveUpgradePatch(Context context, String patchLocation) {
        Tinker.with(context).getPatchListener().onPatchReceived(patchLocation);
    }
}

可以看到,实际上是调用了PatchListener的onPatchReceived方法,我们前面说过,在Tinker中,如果我们没有自定义PatchListener,那么默认的PatchListener就是DefaultPatchListener,这里我们直接去到DefaultPatchListener查看它的onPatchReceived方法

    public int onPatchReceived(String path) {
        //对补丁路径进行校验,进行路径是否存在等等进行判断
        int returnCode = patchCheck(path);

        if (returnCode == ShareConstants.ERROR_PATCH_OK) {
            //校验成功,开启Patch服务
            TinkerPatchService.runPatchService(context, path);
        } else {
            //校验失败,交给LoadReporter加载记录者进行状态记录
            Tinker.with(context).getLoadReporter().onLoadPatchListenerReceiveFail(new File(path), returnCode);
        }
        return returnCode;

    }

可以看到,补丁文件检验成功后会通过TinkerPatchService.runPatchService(context, path)运行TinkerPatchService,代码很简单

   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);
        }
    }

就是将补丁路径和resultServiceClass通过Intent传递,其中resultServiceClass是一个合成补丁结束后执行的Service,它的赋值也在Tinker的install方法中执行,我们后面会讲到它

TinkerPatchService继承了IntentService,这里我们直接看看它的onHandleIntent方法,

     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.");
            }
            //最关键的一步,执行合成补丁!!!!!!
            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;
        //合成补丁结束,运行合成补丁后的Service
        AbstractResultService.runResultService(context, patchResult, getPatchResultExtra(intent));

    }

别看代码长,其实最核心的就是upgradePatchProcessor.tryPatch(context, path, patchResult)而已,其中upgradePatchProcessor其实就是AbstractPatch的实现者,在Tinker的install方法中被赋值,默认为UpgradePatch,这我们前面已经分析过了,那么,合成补丁的过程,实际上就是在UpgradePatchtryPatch方法之中了
这里穿插一个比较有意思的细节,就是上面的 increasingPriority()方法,看方法名的意思大概是提升权重的意思,我们来看一下代码

    private void increasingPriority() {
        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);
        }
    }

现在大家肯定对这段代码不陌生,就是利用系统漏洞进行保活~ 好了回归正题,我们接着看UpgradePatchtryPatch方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值