Androdi热修复之路 ——热修复框架Tinker 源码分析

关于Tinker的使用和接入请读者参考https://github.com/Tencent/tinker自行学习,本文主要从源码的角度分析下Tinker的执行流程,读本文前需要读者了解Android加载机制和ClassLoader原理,另外这里先介绍下Tinker的基本修复原理:

Android程序在启动时会使用ClassLoader加载APK中已存在的类文件,并在内存中生成相应的类入口,程序员出现Bug说明某个类中的代码存在问题,有两种选择修复的方式一种修改原来的类,这种很难实现另一种是用没问题的类去覆盖或替换有Bug的类文件,目前的修复框架基本使用次方案,具体详解参见QQ超级补丁方案,下面带着这些基础知识进入Tinker的源码分析。

1、Tinker初始化

  • Application
@Override
	public void onBaseContextAttached(Context base) {
   
		super.onBaseContextAttached(base);
		TinkerInstaller.install(this);
	}

public static Tinker install(ApplicationLike applicationLike) {
   
        Tinker tinker = new Tinker.Builder(applicationLike.getApplication()).build();
        Tinker.create(tinker);
        tinker.install(applicationLike.getTinkerResultIntent());
        return tinker;
    }

在Application中调用TinkerInstaller.install()初始化,在install()中使用Tinker.Builder()创建Tinker对象,然后调用create()设置Tinker对外单例提供,

  • Tinker.Builder
 
            this.context = context;
            this.mainProcess = TinkerServiceInternals.isInMainProcess(context);
            this.patchProcess = TinkerServiceInternals.isInTinkerPatchServiceProcess(context);
            this.patchDirectory = SharePatchFileUtil.getPatchDirectory(context);
            if (this.patchDirectory == null) {
   
                TinkerLog.e(TAG, "patchDirectory is null!");
                return;
            }
            this.patchInfoFile = SharePatchFileUtil.getPatchInfoFile(patchDirectory.getAbsolutePath());
            this.patchInfoLockFile = SharePatchFileUtil.getPatchInfoLockFile(patchDirectory.getAbsolutePath());
        }

 public Tinker build() {
   
            if (loadReporter == null) {
   
                loadReporter = new DefaultLoadReporter(context);
            }
            if (patchReporter == null) {
   
                patchReporter = new DefaultPatchReporter(context);
            }
            if (listener == null) {
   
                listener = new DefaultPatchListener(context);
            }
            if (tinkerLoadVerifyFlag == null) {
   
                tinkerLoadVerifyFlag = false;
            }

            return new Tinker(context, status, loadReporter, patchReporter, listener, patchDirectory,
                patchInfoFile, patchInfoLockFile, mainProcess, patchProcess, tinkerLoadVerifyFlag);
        }

在TinkerBuiler的构造函数中主要完成了Tinker所需要的文件和文件夹的初始化,在build()中初始化loadReporter、patchReporter、listener,这些属性在Tinker的加载过程中至关重要。

2、下载补丁合成Patch包

  • 在程序中获取到修复补丁包后开始加载
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(),
      Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch.patch");
  1. 通过网络下载或本地保存的方式获取补丁包
  2. 调用TinkerInstaller.onReceiveUpgradePatch()传入补丁路径,通知Tinker有新的补丁包需要合成
  • TinkerInstaller.onReceiveUpgradePatch()
public static void onReceiveUpgradePatch(Context context, String patchLocation) {
   
    Tinker.with(context).getPatchListener().onPatchReceived(patchLocation);
}
  1. TinkerInstaller方法中直接调用getPatcherListener()获取Tinker中设置的PatchListener实例,此处获得的是在TinkerBuilder中默认的DefaultPatchListener,具体的设置过程在加载补丁包时说明;
  • onPatchReceived(String path)
public int onPatchReceived(String path) {
   
    File patchFile = new File(path); //创建补丁包文件
    //(1)、
    int returnCode = patchCheck(path, SharePatchFileUtil.getMD5(patchFile)); // ————————检查的细节
    //(2)
    if (returnCode == ShareConstants.ERROR_PATCH_OK) {
   
        TinkerPatchService.runPatchService(context, path);//启动 TinkerPatchService
    } else {
    //(3)、校验是失败调用LoadReporter通知失败
        Tinker.with(context).getLoadReporter().onLoadPatchListenerReceiveFail(new File(path), returnCode);
    }
    return returnCode;
}

onPatchReceived()中执行以下操作:

  1. 获取补丁包文件后,检查Tinker的配置和path的合法性,主要检测是否开启Tinker、Tinker的服务是否可用、服务是否运行;
  2. 检查通过则启动服务,否则调用默认的LoadReporter通知检车失败
  • TinkerPatchService.runPatchService()
public static void runPatchService(Context context, String path) {
   
       TinkerLog.i(TAG, "run patch service...");
		Intent intent = new Intent(context, TinkerPatchService.class);
		intent.putExtra(PATCH_PATH_EXTRA, path);
		intent.putExtra(RESULT_CLASS_EXTRA, resultServiceClass.getName());
		try {
   
			enqueueWork(context, TinkerPatchService.class, JOB_ID, intent);
		} catch (Throwable thr) {
   
			TinkerLog.e(TAG, "run patch service fail, exception:" + thr);
		}
    }

在runPatchService()中创建Intent保存补丁路径,调用enqueueWork()方法启动TinkerPatchService服务,enqueueWork()在TinkerJobIntentService类中,enqueueWork()中根据Android版本的不同分别使用JobSchedule和PowerManager执行服务

public static void enqueueWork(@NonNull Context context, @NonNull ComponentName component,
                                   int jobId, @NonNull Intent work) {
   
        synchronized (sLock) {
   
            WorkEnqueuer we = getWorkEnqueuer(context, component, true, jobId);
            we.ensureJobId(jobId);
            we.enqueueWork(work);
        }
    }

 static WorkEnqueuer getWorkEnqueuer(Context context, ComponentName cn, boolean hasJobId,
                                        int jobId) {
   
        WorkEnqueuer we = sClassWorkEnqueuer.get(cn); // 从缓存中获取任务
        if (we == null) {
   
            if (Build.VERSION.SDK_INT >= 26
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值