Tinker 合并及加载补丁过程源码分析 (三)

本文深入分析Tinker热修复框架的补丁安装、合并与加载过程。内容涵盖安装补丁包的时序图,从代理Application初始化到UpgradePatch执行合并逻辑;加载补丁的ClassLoader方案,包括TinkerApplication入口、TinkerLoader加载补丁过程;资源与SO文件加载的详细步骤。通过对Tinker源码的解析,了解其在不同Android版本和厂商的兼容策略。
摘要由CSDN通过智能技术生成

声明:本文已授权微信公众号 YYGeeker 独家发布。博主原创文章,转载请注明出处:小嵩的博客

本系列传送门:
微信Tinker 热修复介绍及接入(一)
Tinker 原理深入理解(二)
Tinker 合并及加载补丁过程源码分析 (三)

前言

上篇文章我们讲了Tinker实现的主要原理,本篇文章主要对Tinker源码中补丁安装合并以及加载过程进行分析,本文分析基于Tinker 1.9.8 版本。主要内容有以下几点:

  1. 安装合并补丁包过程。
  2. 加载补丁过程分析。
  3. 加载补丁资源过程分析。
  4. 加载补丁SO文件分析。

一、安装合并补丁包过程分析

时序图如下:

Tinker 安装合并补丁时序图.png

大致流程:

1.1 在代理 Application 中初始化 Tinker 相关。

TinkerApplication.png

我们可以看到Tinker方案中,TinkerApplication继承自Application,也就是说它才是应用真正的Application。Tinker方法使用了ApplicationLike 来代理我们的Application。因此在代理类ApplicationLike 的实现类(demo中是SampleApplicationLike)中对Tinker进行了一些初始化操作。我们可以来看看代码:

SampleApplicationLike.png

TinkerManager中创建了几种Reporter 以及 UpgradePatch 对象。
TinkerManager.png

1.2 调用入口(App主进程)。

当补丁包下发到本地,调用它开始补丁合成:

TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk");
1.3 获取监听器,并调用TinkerPatchService。

TinkerInstaller类中调用Tinker对象实例,然后获取Listener并调用onPatchReceived方法:


public class TinkerInstaller {
   

   //省略部分代码...
    public static void onReceiveUpgradePatch(Context context, String patchLocation) {
   
        Tinker.with(context).getPatchListener().onPatchReceived(patchLocation);
    }
    ...
}
public class DefaultPatchListener implements PatchListener {
   

 //省略部分代码...
    @Override
    public int onPatchReceived(String path) {
   
        File patchFile = new File(path);

        int returnCode = patchCheck(path, SharePatchFileUtil.getMD5(patchFile));//校验Patch 合法性。

        if (returnCode == ShareConstants.ERROR_PATCH_OK) {
   
            TinkerPatchService.runPatchService(context, path);//开启Serive进程服务进行合并Patch
        } else {
   
            Tinker.with(context).getLoadReporter().onLoadPatchListenerReceiveFail(new File(path), returnCode);
        }
        return returnCode;
    }
    
    //省略部分代码...
}
1.4 开启Service服务或者JobScheduler合并Patch。
public class TinkerPatchService {
   
     //省略部分代码...
    public static void runPatchService(final Context context, final String path) {
   
        try {
   
            if (Build.VERSION.SDK_INT < MIN_SDKVER_TO_USE_JOBSCHEDULER) {
   
                runPatchServiceByIntentService(context, path);
            } else {
   
                try {
   
                    runPatchServiceByJobScheduler(context, path);
                } catch (Throwable ignored) {
   
                    // ignored.
                }
                mHandler.postDelayed(new Runnable() {
   
                    @Override
                    public void run() {
   
                        TinkerLog.i(TAG, "check if patch service is running.");
                        if (!TinkerServiceInternals.isTinkerPatchServiceRunning(context)) {
   
                            TinkerLog.w(TAG, "patch service is not running, retry with IntentService.");
                            try {
   
                                runPatchServiceByIntentService(context, path);
                                TinkerLog.i(TAG, "successfully start patch service with IntentService.");
                            } catch (Throwable thr) {
   
                                TinkerLog.e(TAG, "failure to start patch service with IntentService. osver: %s, manu: %s, msg: %s", Build.VERSION.SDK_INT, Build.MANUFACTURER, thr.toString());
                            }
                        }
                    }
                }, TimeUnit.SECONDS.toMillis(5));
            }
        } catch (Throwable throwable) {
   
            TinkerLog.e(TAG, "start patch service fail, exception:" + throwable);
        }
    }
    
}

我们通过源码可以看到,TinkerPatchService 是合并补丁时,比较重要的一个类。runPatchService 方法中,对于Android8.0以下版本使用了 IntentService,并提高服务优先级来避免Patch进程被系统kill掉。针对 Android 8.0 版本额外做了兼容,通过 JobScheduler 以及 Hanlder 循环来保证能够正常开启多进程服务进行补丁合并。

1.5 调用doApplyPatch方法执行合并及校验操作。
public class TinkerPatchService {
   
 //省略部分代码...
private static void doApplyPatch(Context context, Intent intent) {
   
        
        //前面这段代码做了一些校验和琐碎逻辑。
        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);
        }

     //省略部分代码,主要做了一些监听器的回调。
    }
}

可以看到,doApplyPatch 方法中调用了我们在1.1节所述,即 Tinker 初始化时所 new 出来的 UpgradePatch 对象。

1.6 调用UpgradePatch执行合并补丁逻辑。
public class UpgradePatch extends AbstractPatch {
   
    
    @Override
    public boolean tryPatch(Context context, String tempPatchPath, PatchResult patchResult) {
   
       
        //省略部分代码,主要做了一些校验逻辑及文件拷贝删除。
 
        //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;
    }

可以看到,我们的补丁包真正合并的逻辑都是在这里执行的,通过DexDiff、BsDiff、ResDiff 类分别去合并补丁包的Dex类文件,So文件,Resource资源。

1.7 补丁合并完成,回调告知主进程补丁合并结果。
public class TinkerPatchService {
   
 //省略部分代码...
private static void 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值