Tinker再探之TinkerPatch 平台 第一篇
距离上一次学习Tinker热更新:使用Bugly集成的Tinker已经两年了,现在复习一下Tinker。
Tinker对比阿里的 AndFix、美团的 Robust 以及 QZone 的超级补丁方案
功能/名称 | Tinker | QZone | AndFix | Robust |
---|---|---|---|---|
类替换 | yes | yes | no | no |
So替换 | yes | no | no | no |
资源替换 | yes | yes | no | no |
全平台支持 | yes | yes | no | yes |
即时生效 | no | no | yes | yes |
性能损耗 | 较小 | 较大 | 较小 | 较小 |
补丁包大小 | 较小 | 较大 | 一般 | 一般 |
开发透明 | yes | yes | no | no |
复杂度 | 较低 | 较低 | 复杂 | 复杂 |
gradle支持 | yes | no | no | no |
Rom体积 | Dalvik较大 | 较小 | 较小 | 较小 |
成功率 | 较高 | 较高 | 一般 | 最高 |
-
看官方提供对比其他第三方热修复方案,Tinker综合性能最好。只有及时生效,Rom体积有劣势。成功率不是最高的。及时生效就是用户不用重启应用就会生效,个人觉得一般用户对于应用都会开关很多次,所以这项可以忽略。而Rom体积比较大,用户也感知不出来,所以也可以忽略。要是成功率比Robust,就更完美了。
-
同时,由于原理与系统限制,Tinker有以下已知问题:
- 1.Tinker不支持修改AndroidManifest.xml,Tinker不支持新增四大组件(1.9.0支持新增非export的Activity);
- 2.由于Google Play的开发者条款限制,不建议在GP渠道动态更新代码;
- 3.在Android N上,补丁对应用启动时间有轻微的影响;
- 4.不支持部分三星android-21机型,加载补丁时会主动抛出"TinkerRuntimeException:checkDexInstall failed";
- 5.对于资源替换,不支持修改remoteView。例如transition动画,notification icon以及桌面图标。
那么什么是TinkerPatch呢?
Tinker 需要使用者有一个后台可以下发和管理补丁包,并且需要处理传输安全等部署工作,TinkerPatch 平台帮你做了这些工作,提供了补丁后台托管,版本管理,保证传输安全等功能,让你无需搭建一个后台,无需关心部署操作,只需引入一个 SDK 即可立即使用 Tinker。
此外,通过深入研究 Tinker 源码,TinkerTinkerPatch 平台在 Tinker的基础上加入了以下特性:
- 一键傻瓜式接入;无需理解复杂的热修复原理,一行代码即可接入热修复。实现了自动反射 Appliction 与 Library,使用者无需对自己的项目做任何的改动;
- 补丁管理;实现了热补丁的版本管理,补丁的自动重试与异常时自动回退等功能。同时我们可以简单实现条件下发补丁,在出现异常情况时,我们也可以快速回滚补丁;
- 编译优化;简化了 Tinker 的编译复杂度,实现了备份路径选择,功能开关等功能。
TinkerPatch 平台在 Github 为大家提供了各种各样的 Sample,大家可点击前往 [TinkerPatch Github].
为什么使用 TinkerPatch 平台?
市面上可能还有其他的一些热补丁服务,为什么我们需要选择 TinkerPatch 平台呢?
- 研发实力雄厚;Tinker 在微信的数亿用户上得到验证,它的稳定性与性能值得信赖。TinkerPatch 平台作为 Tinker 项目贡献者与管理者之一,在 Tinker 基础上开发了许多方便使用者的特性;
- 服务全面快速;TinkerPatch 平台客户关于热修复使用过程的所有问题在工作日内一个小时内响应,提供您满意的服务;
- 稳定可靠;TinkerPatch 平台上传的补丁文件都会保存在七牛云存储上,客户端 APP 只跟七牛服务器通讯,支持高并发,CDN分布全国,速度和稳定性有保证。
TinkerPatch 平台将与 Tinker 一起继续优化,四大组件代理/ABTest/安全模式等功能都将陆续推出,欢迎大家咨询。
第一步 添加 gradle 插件依赖 - 在Project的build.gradle
buildscript {
repositories {
jcenter()
}
dependencies {
// TinkerPatch 插件
classpath "com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:1.2.13"
}
}
第二步 集成 TinkerPatch SDK
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
//若使用annotation需要单独引用,对于tinker的其他库都无需再引用
annotationProcessor("com.tinkerpatch.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true }
compileOnly("com.tinkerpatch.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true }
implementation("com.tinkerpatch.sdk:tinkerpatch-android-sdk:${TINKERPATCH_VERSION}") { changing = true }
}
apply from: 'tinkerpatch.gradle'
- 在
gradle.properties
中
TINKER_VERSION=1.9.2
TINKERPATCH_VERSION=1.2.2
第三步 配置 tinkerpatchSupport 参数
在Module的build.gradle
同级目录下创建tinkerpatch.gradle
:
apply plugin: 'tinkerpatch-support'
/**
* TODO: 请按自己的需求修改为适应自己工程的参数
*/
def bakPath = file("${buildDir}/bakApk/")
def baseInfo = "app-1.0.0-1112-12-49-34"
def variantName = "debug"
/**
* 对于插件各参数的详细解析请参考
* http://tinkerpatch.com/Docs/SDK
*/
tinkerpatchSupport {
/** 可以在debug的时候关闭 tinkerPatch **/
/** 当disable tinker的时候需要添加multiDexKeepProguard和proguardFiles,
这些配置文件本身由tinkerPatch的插件自动添加,当你disable后需要手动添加
你可以copy本示例中的proguardRules.pro和tinkerMultidexKeep.pro,
需要你手动修改'tinker.sample.android.app'本示例的包名为你自己的包名, com.xxx前缀的包名不用修改
**/
tinkerEnable = true
reflectApplication = false
/**
* 是否开启加固模式,只能在APK将要进行加固时使用,否则会patch失败。
* 如果只在某个渠道使用了加固,可使用多flavors配置
**/
protectedApp = false
/**
* 实验功能
* 补丁是否支持新增 Activity (新增Activity的exported属性必须为false)
**/
supportComponent = true
autoBackupApkPath = "${bakPath}"
appKey = "f938475486f91936"
/** 注意: 若发布新的全量包, appVersion一定要更新 **/
appVersion = "1.0.0"
def pathPrefix = "${bakPath}/${baseInfo}/${variantName}/"
def name = "${project.name}-${variantName}"
baseApkFile = "${pathPrefix}/${name}.apk"
baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt"
baseResourceRFile = "${pathPrefix}/${name}-R.txt"
/**
* 若有编译多flavors需求, 可以参照: https://github.com/TinkerPatch/tinkerpatch-flavors-sample
* 注意: 除非你不同的flavor代码是不一样的,不然建议采用zip comment或者文件方式生成渠道信息(相关工具:walle 或者 packer-ng)
**/
}
/**
* 用于用户在代码中判断tinkerPatch是否被使能
*/
android {
defaultConfig {
buildConfigField "boolean", "TINKER_ENABLE", "${tinkerpatchSupport.tinkerEnable}"
}
}
/**
* 一般来说,我们无需对下面的参数做任何的修改
* 对于各参数的详细介绍请参考:
* https://github.com/Tencent/tinker/wiki/Tinker-接入指南
*/
tinkerPatch {
ignoreWarning = false
useSign = true
dex {
dexMode = "jar"
pattern = ["classes*.dex"]
loader = []
}
lib {
pattern = ["lib/*/*.so"]
}
res {
pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
ignoreChange = []
largeModSize = 100
}
packageConfig {
}
sevenZip {
zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
// path = "/usr/local/bin/7za"
}
buildConfig {
keepDexApply = false
}
}
同步后,出现错误一:
方法过时,使用新方法
说明Tinker库有的方法过时了,我们可以不理会,或者回退gradle到3.2.1,等Tinker更新这些过时的方法。
在tinkerpatch.gradle
中我们只需要更改appKey
和对应的打包名称baseInfo
。其他的一般不需要更改,但有的确实需要更改的,可以参考gradle参数配置详解
第四步 初始化 TinkerPatch SDK
- 在
tinkerpatch.gradle
中配置参数为reflectApplication = true
的情况
- 在
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 我们可以从这里获得Tinker加载过程的信息
ApplicationLike tinkerApplicationLike = TinkerPatchApplicationLike.getTinkerPatchApplicationLike();
// 初始化TinkerPatch SDK, 更多配置可参照API章节中的,初始化SDK
TinkerPatch.init(tinkerApplicationLike)
.reflectPatchLibrary()
.setPatchRollbackOnScreenOff(true)
.setPatchRestartOnSrceenOff(true)
.setFetchPatchIntervalByHours(3);
// 每隔3个小时(通过setFetchPatchIntervalByHours设置)去访问后台时候有更新,通过handler实现轮训的效果
TinkerPatch.with().fetchPatchUpdateAndPollWithInterval();
}
}
-
- 在
tinkerpatch.gradle
中配置参数为reflectApplication = flase
的情况
- 在
第五步 使用步骤
- 1.运行
assembleRelease
构建基准包(请在发布前确保更新tinkerpatch.gradle
中的appVersion
),tinkerPatch会基于你填入的autoBackupApkPath
自动备份基础包信息到相应的文件夹,包含:apk文件、R.txt文件和mapping.txt文件 (注:mapping.txt是proguard的产物,如果你没有开启proguard则不会有这个文件)
双击assembleRelease命令打包
文件夹名称相当于版本名称appVersion
- 2.若想发布补丁包, 只需将自动保存下来的文件分别填到
tinkerpatch.gradle
中的baseApkFile、baseProguardMappingFile和baseResourceRFile 参数中; -
3.运行 tinkerPatchRelease task 构建补丁包,补丁包将位于 build/outputs/tinkerPatch下。
image.png
image.png
配置差不多完成了,那么配置中的appkey和如何发布可以参考:平台使用说明
运行项目:
- 错误一:
applicationLike must not be null.
原因:是因为配置的tinkerpatch.gradle
中的reflectApplication = flase
,改为reflectApplication = true
- 错误二:
> old apk F:\project\MLVBSDK-master\Android\TinkerPatchDemo\app\build\bakApk/app-1.0.0-0715-15-35-51/release//app-release.apk is not exist, you must set the correct old apk value!
原因:是因为配置的tinkerpatch.gradle
中的baseInfo
不正确,改为def baseInfo = app-1.0.0-0715-16-20-04
,也就是打的基准包的文件名称
-
错误三
we build app apk with apply resource mapping file F:\project\MLVBSDK-master\Android\TinkerPatchDemo\app\build\bakApk/app-1.0.0-0715-16-20-04/release//app-release-R.txt
原因:原来是我们mapping.txt是proguard的产物,如果你没有开启proguard则不会有这个文件,所以我们注释调映射一行 -
错误四
配置签名can't the get signConfig for this build
原因:没有配置签名文件,那么配置签名文件吧
-
错误五:
Execution failed for task ':app:tinkerProcessReleaseResourceId'. java.io.FileNotFoundException: F:\project\MLVBSDK-master\Android\TinkerPatchDemo\app\build\intermediates\tinker_intermediates\values_backup
原因:说是values_backup
没找到,解决方案一
经过5连跪,终于出现了官方说的第三步,运行 tinkerPatchRelease task 构建补丁包。所谓的补丁包对应的文件夹。补丁包将位于 build/outputs/tinkerPatch下:
tinkerPatch我估计
patch_signed.apk
应该就是补丁包,去TinkerPatch后台把这个包以开发预览的方式发布上去开发预览
- 实验一:重启一下应用,没有反应。界面还是只有一个
Hello World!
,哦,忘记说了,我修改的内容其实就是在布局文件中加了一段话-- 我是补丁包1.0
. -
实验二:哦,记得要官方下载的DebugTool工具的开关,才能用开发预览模式,打开了,还是无效。
打开开发模式 - 实验三:哦,突然想起Application中默认设置的3小时更新补丁,改为立即拉取补丁
TinkerPatch.with().fetchPatchUpdate(true);
- 实验四:重启N次,依然无反应,后台去看说是下载补丁包失败,日志中看说是签名不对,这个时候才想起自己是debug包,而打的基础包和补丁包是针对relase包的,所以把这个基础包app-relase.apk传到手机上进行安装,安装后打开第一次还是Hello World~!重启后生效。
经过4连跪,重启应用,总算多了几个字:我是补丁包1.0
后台看看,确实成功下发了:
实时监控
没错,这就是Tinker的精髓,不重新下载apk,即可实现修改文字的效果。
下一篇Tinker再探之TinkerPatch 平台 第二篇我们将学习1.在线参数获取,2.实时监控,3.图片补丁,4.新增Activity的相关功能
文档参考:TinkerPatch 平台介绍
Demo参考:TinkerPatch相关Demo