源码阅读--腾讯Tinker热修复框架
来源:互联网 发布:域名转让流程 编辑:程序博客网 时间:2024/06/05 15:37
先要配置这么一段话:后面会谈到为什么以及如何改变这些默认设置
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热修复框架
- [Android]腾讯Tinker热修复框架简单使用
- 接入热修复框架TinKer
- 热修复框架AndFix【源码阅读】
- 热修复 tinker接入及源码分析
- Tinker 热修复框架 简单上手教程
- Tinker 热修复框架 简单上手教程
- Tinker 热修复框架 简单上手教程
- Android 热修复框架 Tinker ( 一 )
- Android 热修复框架 Tinker ( 二 )
- Android 热修复框架 Tinker ( 三 )
- android热修复框架Tinker(一)
- Tinker 热修复框架 简单上手教程
- 热修复框架Tinker快速集成
- 热修复框架-Tinker接入常见问题
- Android热修复框架 Tinker 接入
- Tinker 热修复框架 简单上手教程
- Android热修复框架Tinker初体验
- 从小菜鸟到老菜鸟
- android 6.0 7.0 不一样的处理权限适配
- Java+Selenium3框架设计准备篇10-用非PageFactory实现POM
- SoapUI、Jmeter、Postman三种接口测试工具的比较分析——灰蓝
- sql 报错:语句被终止。完成执行语句前已用完最大递归 100。
- 源码阅读--腾讯Tinker热修复框架
- 在Activity中调用Service的方法——本地服务
- hibernate主配置文件的数据库属性所在的
- poi excel 数字读为dobul或科学数字 处理办法
- Bridge to the digital world —— AR算法技术分享
- MySQL中的行级锁,表级锁,页级锁
- oracle同义词创建(synonym)
- Linux常用指令
- CURL使用