可选的自定义类

在Tinker中你可以自定义一些类,它们需要在构造Tinker实例时作为参数传递,在TinkerManagerinstallTinker中,你可以根据自己的需要自定义其中的一些类:

  1. //or you can just use DefaultLoadReporter
  2. LoadReporter loadReporter = new SampleLoadReporter(appLike.getApplication());
  3. //or you can just use DefaultPatchReporter
  4. PatchReporter patchReporter = new SamplePatchReporter(appLike.getApplication());
  5. //or you can just use DefaultPatchListener
  6. PatchListener patchListener = new SamplePatchListener(appLike.getApplication());
  7. //you can set your own upgrade patch if you need
  8. AbstractPatch upgradePatchProcessor = new UpgradePatch();
  9. TinkerInstaller.install(appLike,
  10. loadReporter, patchReporter, patchListener,
  11. SampleResultService.class, upgradePatchProcessor);

你也可以使用sampleInstallTinker,即全部使用默认参数。

各个类具体的功能与使用方法如下:

自定义LoadReporter类

LoadReporter类定义了Tinker在加载补丁时的一些回调,我们为你提供了默认实现DefaultLoadReporter.java.

一般来说, 你可以继承DefaultLoadReporter实现你自己感兴趣的事件回调,例如SampleLoadReporter.java.

我们在sample中也写了一个默认的回调上报例子,可参考SampleTinkerReport.

需要注意的有以下两点

  • 回调运行在加载的进程,它有可能是各个不一样的进程。我们可以通过tinker.isMainProcess或者tinker.isPatchProcess知道当前是否是主进程,patch补丁合成进程。
  • 回调发生的时机是我们调用installTinker之后,某些进程可能并不需要installTinker。现对各个回调作简要的说明:
函数描述
onLoadResult这个是无论加载失败或者成功都会回调的接口,它返回了本次加载所用的时间、返回码等信息。默认我们只是简单的输出这个信息,你可以在这里加上监控上报逻辑。
onLoadPatchListenerReceiveFail所有的补丁合成请求都需要先通过PatchListener的检查过滤。这次检查不通过的回调,它运行在发起请求的进程。默认我们只是打印日志
onLoadPatchVersionChanged补丁包版本升级的回调,只会在主进程调用。默认我们会杀掉其他所有的进程(保证所有进程代码的一致性),并且删掉旧版本的补丁文件。
onLoadFileNotFound在加载过程中,发现部分文件丢失的回调。默认若是dex,dex优化文件或者lib文件丢失,我们将尝试从补丁包去修复这些丢失的文件。若补丁包或者版本文件丢失,将卸载补丁包。
onLoadFileMd5Mismatch部分文件的md5与meta中定义的不一致。默认我们为了安全考虑,依然会清空补丁。
onLoadPatchInfoCorruptedpatch.info是用来管理补丁包版本的文件,这是info文件损坏的回调。默认我们会卸载补丁包,因为此时我们已经无法恢复了。
onLoadPackageCheckFail加载过程补丁包的检查失败,这里可以通过错误码区分,例如签名校验失败、tinkerId不一致等原因。默认我们将会卸载补丁包
onLoadException在加载过程捕捉到异常,十分希望你可以把错误信息反馈给我们。默认我们会直接卸载补丁包
onLoadInterpret系统OTA后,为了加快补丁的执行,我们会采用解释模式来执行补丁。

所有的错误码都定义在ShareConstants.javaonLoadPackageCheckFail 的相关错误码解析如下:

错误码数值描述
ERROR_PACKAGE_CHECK_SIGNATURE_FAIL-1签名校验失败
ERROR_PACKAGE_CHECK_PACKAGE_META_NOT_FOUND-2找不到"assets/package_meta.txt"文件
ERROR_PACKAGE_CHECK_DEX_META_CORRUPTED-3"assets/dex_meta.txt"信息损坏
ERROR_PACKAGE_CHECK_LIB_META_CORRUPTED-4"assets/so_meta.txt"信息损坏
ERROR_PACKAGE_CHECK_APK_TINKER_ID_NOT_FOUND-5找不到基准apk AndroidManifest中的TINKER_ID
ERROR_PACKAGE_CHECK_PATCH_TINKER_ID_NOT_FOUND-6找不到补丁中"assets/package_meta.txt"中的TINKER_ID
ERROR_PACKAGE_CHECK_TINKER_ID_NOT_EQUAL-7基准版本与补丁定义的TINKER_ID不相等
ERROR_PACKAGE_CHECK_RESOURCE_META_CORRUPTED-8"assets/res_meta.txt"信息损坏
ERROR_PACKAGE_CHECK_TINKERFLAG_NOT_SUPPORT-9tinkerFlag不支持补丁中的某些类型的更改,例如补丁中存在资源更新,但是使用者指定不支持资源类型更新。

onLoadException的错误码具体如下:

错误码数值描述
ERROR_LOAD_EXCEPTION_UNKNOWN-1没有捕获到的java crash
ERROR_LOAD_EXCEPTION_DEX-2在加载dex过程中捕获到的crash
ERROR_LOAD_EXCEPTION_RESOURCE-3在加载res过程中捕获到的crash
ERROR_LOAD_EXCEPTION_UNCAUGHT-4没有捕获到的非java crash,这个是补丁机制的安全模式

回调中定义的fileType定义如下:

文件类型数值描述
TYPE_PATCH_FILE1补丁文件
TYPE_PATCH_INFO2"patch.info"补丁版本配置文件
TYPE_DEX3在Dalvik合成全量的Dex文件
TYPE_DEX_OPT4odex文件
TYPE_LIBRARY5library文件
TYPE_RESOURCE6资源文件

加载过程的具体的错误类型与错误码可查看DefaultLoadReporter.java的注释。

对于onLoadPatchVersionChanged与onLoadFileNotFound的复写要较为谨慎,因为版本升级杀掉其他进程与文件丢失发起恢复任务,都是我认为比较重要的操作。

自定义PatchReporter类

PatchReporter类定义了Tinker在修复或者升级补丁时的一些回调,我们为你提供了默认实现DefaultPatchReporter.java.

一般来说, 你可以继承DefaultPatchReporter实现你自己感兴趣的事件回调,例如SamplePatchReporter.java.

需要注意的是:

函数描述
onPatchResult这个是无论补丁合成失败或者成功都会回调的接口,它返回了本次合成的类型,时间以及结果等。默认我们只是简单的输出这个信息,你可以在这里加上监控上报逻辑。
onPatchServiceStart这个是Patch进程启动时的回调,我们可以在这里进行一个统计的工作。
onPatchPackageCheckFail补丁合成过程对输入补丁包的检查失败,这里可以通过错误码区分,例如签名校验失败、tinkerId不一致等原因。默认我们会删除临时文件。
onPatchVersionCheckFail对patch.info的校验版本合法性校验。若校验失败,默认我们会删除临时文件。
onPatchTypeExtractFail从补丁包与原始安装包中合成某种类型的文件出现错误,默认我们会删除临时文件。
onPatchDexOptFail对合成的dex文件提前进行dexopt时出现异常,默认我们会删除临时文件。
onPatchInfoCorruptedpatch.info是用来管理补丁包版本的文件,这是在更新info文件时发生损坏的回调。默认我们会卸载补丁包,因为此时我们已经无法恢复了。
onPatchException在补丁合成过程捕捉到异常,十分希望你可以把错误信息反馈给我们。默认我们会删除临时文件,并且将tinkerFlag设为不可用。

PatchReporter中onPatchPackageCheckFail的错误码与LoadReporter的一致。

自定义PatchListener类

PatchListener类是用来过滤Tinker收到的补丁包的修复、升级请求,也就是决定我们是不是真的要唤起:patch进程去尝试补丁合成。我们为你提供了默认实现DefaultPatchListener.java

一般来说, 你可以继承DefaultPatchListener并且加上自己的检查逻辑,例如SamplePatchListener.java

若检查成功,我们会调用TinkerPatchService.runPatchService唤起:patch进程,去尝试完成补丁合成操作。反之,会回调检验失败的接口。事实上,你只需要复写patchCheck函数即可。若检查失败,会在LoadReporter的onLoadPatchListenerReceiveFail中回调。

  1. public int patchCheck(String path)

以DefaultPatchListener为例,说明默认我们检查的条件,你可以定义自己的错误码,也可以沿用这里的错误码。

错误码数值描述
ERROR_PATCH_DISABLE-1当前tinkerFlag为不可用状态。
ERROR_PATCH_NOTEXIST-2输入的临时补丁包文件不存在。
ERROR_PATCH_RUNNING-3当前:patch补丁合成进程正在运行。
ERROR_PATCH_INSERVICE-4不能在:patch补丁合成进程,发起补丁的合成请求。
ERROR_PATCH_JIT-5补丁不支持 N 之前的 JIT 模式。
ERROR_PATCH_ALREADY_APPLY-6补丁已经应用。
ERROR_PATCH_RETRY_COUNT_LIMIT-7补丁超过重试次数。
其他在SamplePatchListener里面,我们还检查了当前Rom剩余空间,最大内存,是否是GooglePlay渠道等条件。

自定义AbstractResultService类

AbstractResultService类是:patch补丁合成进程将合成结果返回给主进程的类。我们为你提供了默认实现DefaultTinkerResultService.java

一般来说, 你可以继承DefaultTinkerResultService实现自己的回调,例如SampleResultService.java当然,你也需要在AndroidManifest上添加你的Service。

  1. <service
  2. android:name=".service.SampleResultService"
  3. android:exported="false"
  4. />

默认我们在DefaultTinkerResultService会杀掉:patch进程,假设当前是补丁升级并且成功了,我们会杀掉当前进程,让补丁包更快的生效。若是修复类型的补丁包并且失败了,我们会卸载补丁包。下面对PatchResult的定义做详细说明:

函数描述
isSuccess补丁合成操作是否成功。
rawPatchFilePath原始的补丁包路径。
costTime本次补丁合成的耗时。
e本次补丁合成是否出现异常,null为没有异常。
patchVersion补丁文件的md5, 有可能为空@Nullable。

SampleResultService.java中,我们没有立刻杀掉当前进程去应用补丁,而选择在当前应用在退入后台或手机锁屏时这两个时机。你也可以在自杀前,通过发送service或者broadcast inent来尽快重启进程。

自定义TinkerLoader类

TinkerLoader类是用来加载补丁的核心类,你可以实现自己的加载逻辑。但是一般不建议那么做,如果你一定要你需要保证以下两条规则:

  • 将你的实现的类以及它用到的所有类都加入到dex.loader中;
  • 保证上述的类都在main dex中。只要简单的将loaderClass参数中的"com.tencent.tinker.loader.TinkerLoader",换成你的实现类的名称即可,这里只能传递字符串。

  1. @DefaultLifeCycle(application = ".SampleApplication", //application类名flags = ShareConstants.TINKER_ENABLE_ALL, //tinkerFlagsloaderClass = "com.tencent.tinker.loader.TinkerLoader") //loaderClassName, 我们这里使用默认即可!public class SampleApplicationLike extends DefaultApplicationLike

Tinker Notification id设置

为了提高TinkerPatchService的进程优先级,我们将它设置为Foreground。对于sdk>18的版本,使用innerService方式使通知栏不会显示。

Warning, 这里占用了id为-1119860829.若你的app存在与它相同的id, 可以使用以下API重新设置

  1. TinkerPatchService.setTinkerNotificationId(id);
  2. Tinker.with(context).setPatchServiceNotificationId(id);

自定义UpgradePatch类

UpgradePatch类是用来升级当前补丁包的处理类,一般来说你也不需要复写它。

可以看到整个Tinker框架非常灵活,基本所有的逻辑都放在可复写的类或回调中,你可以轻松的完成自身需要的自定义工作。

你可以根据需要自定义以上的一些类,然后我们继续学习Tinker API概览