源码阅读--腾讯Tinker热修复框架
2017-07-19 16:44
357 查看
先要配置这么一段话:后面会谈到为什么以及如何改变这些默认设置
通过下面这句话启动热修复
看一下接下来的函数调用:
这里的onHandleIntent是很重要的。所以拿出来解释
看到tinker.getPatchReporter()了么?就是我们一开始配置的DefaultPatchReporter。但是会发现onPatchServiceStart,onPatchResult就打了点Log。如果觉得还不够,我们可以实现自定义接口—只要继承DefaultPatchReporter并且实现他的几个接口(比如弹出提示语增加界面友好度)
好了,接下来看tryPatch和runResultService函数
所以热修复的核心如下:拿到补丁apk,检查签名,MD5。然后对dex,library,resource文件分别进行解压比较覆盖更新。(至于如何解压覆盖安装的,我只看到一个有zip字眼的类,具体怎么处理的我也搞不清了)
这句话中的getPatchResultExtra返回的是RESULT_CLASS_EXTRA对应的字符串
看一下resultServiceClass是啥?
Class
说白了就是可以写个Service继承AbstractResultService来实现onPatchResult。库里面有个默认的DefaultTinkerResultService,但是只有删除文件。我们可以自己写一个Service继承DefaultTinkerResultService来让整个程序可以继续进行(一般加载完补丁会有些界面上的调整)
public static void installTinker(ApplicationLike appLike) { //or you can just use DefaultLoadReporter LoadReporter loadReporter = new DefaultLoadReporter(appLike.getApplication()); //or you can just use DefaultPatchReporter PatchReporter patchReporter = new DefaultPatchReporter(appLike.getApplication()); //or you can just use DefaultPatchListener PatchListener patchListener = new DefaultPatchListener(appLike.getApplication()); //you can set your own upgrade patch if you need AbstractPatch upgradePatchProcessor = new UpgradePatch(); TinkerInstaller.install(appLike, loadReporter, patchReporter, patchListener, TinkerResultService.class, upgradePatchProcessor); isInstalled = true; }
通过下面这句话启动热修复
TinkerInstaller.onReceiveUpgradePatch(mContext, saveFileName);
看一下接下来的函数调用:
public static void onReceiveUpgradePatch(Context context, String patchLocation) { Tinker.with(context).getPatchListener().onPatchReceived(patchLocation); } public int onPatchReceived(String path) { int returnCode = patchCheck(path); if (returnCode == ShareConstants.ERROR_PATCH_OK) { TinkerPatchService.runPatchService(context, path); } else { Tinker.with(context).getLoadReporter().onLoadPatchListenerReceiveFail(new File(path), returnCode); } return returnCode; } public static void runPatchService(Context context, String path) { try { Intent intent = new Intent(context, TinkerPatchService.class);//注意这里,是一个IntentService,所以调用onHandleIntent 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); } }
这里的onHandleIntent是很重要的。所以拿出来解释
//代码有缩减。重要的函数已经通过*标注 protected void onHandleIntent(Intent intent) { final Context context = getApplicationContext(); Tinker tinker = Tinker.with(context); tinker.getPatchReporter().onPatchServiceStart(intent);//************************************* String path = getPatchPathExtra(intent);//获取存放在intent里面的PATCH_PATH_EXTRA的值,说白了就是补丁的路径 File patchFile = new File(path); long begin = SystemClock.elapsedRealtime(); boolean result; long cost; Throwable e = null; PatchResult patchResult = new PatchResult(); try { 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; AbstractResultService.runResultService(context, patchResult, getPatchResultExtra(intent));//************************************* }
看到tinker.getPatchReporter()了么?就是我们一开始配置的DefaultPatchReporter。但是会发现onPatchServiceStart,onPatchResult就打了点Log。如果觉得还不够,我们可以实现自定义接口—只要继承DefaultPatchReporter并且实现他的几个接口(比如弹出提示语增加界面友好度)
好了,接下来看tryPatch和runResultService函数
(1)tryPatch
upgradePatchProcessor其实就是AbstractPatch。最终在UpgradePatch中实现public boolean tryPatch(Context context, String tempPatchPath, PatchResult patchResult) { Tinker manager = Tinker.with(context); final File patchFile = new File(tempPatchPath); //检查签名 ShareSecurityCheck signatureCheck = new ShareSecurityCheck(context); int returnCode = ShareTinkerInternals.checkTinkerPackage(context, manager.getTinkerFlags(), patchFile, signatureCheck); if (returnCode != ShareConstants.ERROR_PACKAGE_CHECK_OK) { TinkerLog.e(TAG, "UpgradePatch tryPatch:onPatchPackageCheckFail"); manager.getPatchReporter().onPatchPackageCheckFail(patchFile, returnCode); return false; } //it is a new patch, so we should not find a exist SharePatchInfo oldInfo = manager.getTinkerLoadResultIfPresent().patchInfo; String patchMd5 = SharePatchFileUtil.getMD5(patchFile); if (patchMd5 == null) { TinkerLog.e(TAG, "UpgradePatch tryPatch:patch md5 is null, just return"); return false; } //use md5 as version patchResult.patchVersion = patchMd5; SharePatchInfo newInfo; //already have patch if (oldInfo != null) { if (oldInfo.oldVersion == null || oldInfo.newVersion == null) { TinkerLog.e(TAG, "UpgradePatch tryPatch:onPatchInfoCorrupted"); manager.getPatchReporter().onPatchInfoCorrupted(patchFile, oldInfo.oldVersion, oldInfo.newVersion); return false; } if (!SharePatchFileUtil.checkIfMd5Valid(patchMd5)) { TinkerLog.e(TAG, "UpgradePatch tryPatch:onPatchVersionCheckFail md5 %s is valid", patchMd5); manager.getPatchReporter().onPatchVersionCheckFail(patchFile, oldInfo, patchMd5); return false; } newInfo = new SharePatchInfo(oldInfo.oldVersion, patchMd5, Build.FINGERPRINT); } else { newInfo = new SharePatchInfo("", patchMd5, Build.FINGERPRINT); } //check ok, we can real recover a new patch final String patchDirectory = manager.getPatchDirectory().getAbsolutePath(); TinkerLog.i(TAG, "UpgradePatch tryPatch:patchMd5:%s", patchMd5); final String patchName = SharePatchFileUtil.getPatchVersionDirectory(patchMd5); final String patchVersionDirectory = patchDirectory + "/" + patchName; TinkerLog.i(TAG, "UpgradePatch tryPatch:patchVersionDirectory:%s", patchVersionDirectory); //it is a new patch, we first delete if there is any files //don't delete dir for faster retry // SharePatchFileUtil.deleteDir(patchVersionDirectory); //copy file File destPatchFile = new File(patchVersionDirectory + "/" + SharePatchFileUtil.getPatchVersionFile(patchMd5)); try { // 检查MD5 if (!patchMd5.equals(SharePatchFileUtil.getMD5(destPatchFile))) { SharePatchFileUtil.copyFileUsingStream(patchFile, destPatchFile); TinkerLog.w(TAG, "UpgradePatch copy patch file, src file: %s size: %d, dest file: %s size:%d", patchFile.getAbsolutePath(), patchFile.length(), destPatchFile.getAbsolutePath(), destPatchFile.length()); } } catch (IOException e) { ...... } //----------------这里真正开始修复补丁 //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; } ...... return true; }
所以热修复的核心如下:拿到补丁apk,检查签名,MD5。然后对dex,library,resource文件分别进行解压比较覆盖更新。(至于如何解压覆盖安装的,我只看到一个有zip字眼的类,具体怎么处理的我也搞不清了)
(2)runResultService
AbstractResultService.runResultService(context, patchResult, getPatchResultExtra(intent));这句话中的getPatchResultExtra返回的是RESULT_CLASS_EXTRA对应的字符串
public static void runResultService(Context context, PatchResult result, String resultServiceClass) { try { Intent intent = new Intent(); intent.setClassName(context, resultServiceClass); intent.putExtra(RESULT_EXTRA, result); context.startService(intent); } catch (Throwable throwable) { TinkerLog.e(TAG, "run result service fail, exception:" + throwable); } }
看一下resultServiceClass是啥?
Class
@Override protected void onHandleIntent(Intent intent) { if (intent == null) { TinkerLog.e(TAG, "AbstractResultService received a null intent, ignoring."); return; } PatchResult result = (PatchResult) ShareIntentUtil.getSerializableExtra(intent, RESULT_EXTRA); onPatchResult(result); } public abstract void onPatchResult(PatchResult result);
说白了就是可以写个Service继承AbstractResultService来实现onPatchResult。库里面有个默认的DefaultTinkerResultService,但是只有删除文件。我们可以自己写一个Service继承DefaultTinkerResultService来让整个程序可以继续进行(一般加载完补丁会有些界面上的调整)
相关文章推荐
- 零基础学习腾讯热修复框架Tinker
- 源码分析微信热修复框架Tinker的类加载过程
- 腾讯热修复框架tinker
- Android探索之旅(第二十五篇)腾讯热修复框架Tinker与阿里第三代热修复框架Sophix对比
- 源码分析微信热修复框架Tinker的类加载过程 .
- 热修复框架AndFix【源码阅读】
- [Android]腾讯Tinker热修复框架简单使用
- 源码分析微信热修复框架Tinker的类加载过程
- 关于使用腾讯 Bugly 平台 Tinker开源热修复框架的 项目集成
- Android热修复框架 Tinker 接入
- CI框架源码阅读笔记8 控制器Controller.php
- J.U.C并发框架源码阅读(三)ReentrantLock
- J.U.C并发框架源码阅读(六)ConditionObject
- J.U.C并发框架源码阅读(八)ArrayBlockingQueue
- CI框架源码阅读笔记1 - 环境准备、基本术语和框架流程
- CI框架源码阅读,系统常量文件constants.php的配置
- CI框架源码阅读---------Lang.php
- CI框架源码阅读笔记5 基准测试 BenchMark.php
- yaf源码阅读之 -- 框架基本生命周期 (yaf.c)
- CI框架源码阅读笔记8 控制器Controller.php