第五章 android热更新系列文章 之 Tinker 介绍

目录
6. AndroidStudio gradle构建脚本下的Tinker集成
7. apkPatch
补丁包文件实际生产环境中的下载和安装解决方案
8. 总结了一些常见问题,你很可能会遇到,不要紧张可以看看下面有对应的解决方案没
8.1 通过AndroidStudio方式生成差异包的时候如果不小心修改了XML会报下面的错误
8.3 生成差异包时尝试更改 AndroidManifest
清单文件,出现编译期报错
Tinker是微信官方的Android热补丁解决方案,它支持动态下发代码、So库以及资源,让应用能够在不需要重新安装的情况下实现更新。当然,你也可以使用Tinker来更新你的插件。
它主要包括以下几个部分:
gradle编译插件: tinker-patch-gradle-plugin
核心sdk库: tinker-android-lib
非gradle编译用户的命令行版本: tinker-patch-cli.jar
Tinker是由腾讯推出的一款支持dex、依赖库、资源文件,清单文件更新的android热修复解决方案,使用tinker可以解决发版本完成版本更新,和阿里系的Andfix相比tinker的使用场景法更为广(包括了dex、依赖库、资源文件,清单文件),修复的成功率更高,听起来是不是很炫酷,
2. 完整的demo下载,快速体验
附完整的demo链接 : https://github.com/benchegnzhou/TinkerDemo
3. Tinker官方开源项目链接
Tinker官方开源项目链接 : https://github.com/Tencent/tinker/
4. 为什么使用Tinker
当前市面的热补丁方案有很多,其中比较出名的有阿里的AndFix、美团的Robust以及QZone的超级补丁方案。但它们都存在无法解决的问题,这也是正是我们推出Tinker的原因。
比较项 | Tinker | QZone | AndFix | Robust |
---|---|---|---|---|
类替换 | yes | yes | no | no |
So替换 | yes | no | no | no |
资源替换 | yes | yes | no | no |
全平台支持 | yes | yes | yes | yes |
即时生效 | no | no | yes | yes |
性能损耗 | 较小 | 较大 | 较小 | 较小 |
补丁包大小 | 较小 | 较大 | 一般 | 一般 |
开发透明 | yes | yes | no | no |
复杂度 | 较低 | 较低 | 复杂 | 复杂 |
gradle支持 | yes | no | no | no |
Rom体积 | 较大 | 较小 | 较小 | 较小 |
成功率 | 较高 | 较高 | 一般 | 最高 |
总的来说:
AndFix作为native解决方案,首先面临的是稳定性与兼容性问题,更重要的是它无法实现类替换,它是需要大量额外的开发成本的;
Robust兼容性与成功率较高,但是它与AndFix一样,无法新增变量与类只能用做的bugFix方案;
Qzone方案可以做到发布产品功能,但是它主要问题是插桩带来Dalvik的性能问题,以及为了解决Art下内存地址问题而导致补丁包急速增大的。
特别是在Android N之后,由于混合编译的inline策略修改,对于市面上的各种方案都不太容易解决。而Tinker热补丁方案不仅支持类、So以及资源的替换,它还是2.X-8.X(1.9.0以上支持8.X)的全平台支持。利用Tinker我们不仅可以用做bugfix,甚至可以替代功能的发布。Tinker已运行在微信的数亿Android设备上,那么为什么你不使用Tinker呢?
5. Tinker的缺陷及不足
Tinker的确非常的强大,但是也不是没有局限性的,比如不支持XML文件的修改就显得十分的鸡肋了,同时也无法修改APP的图标和APP的名称(这对于一个APP的节日营销是致命的存在),同时还存在以下已知的问题
Tinker的已知问题
由于原理与系统限制,Tinker有以下已知问题:
Tinker不支持修改AndroidManifest.xml,Tinker不支持新增四大组件(1.9.0支持新增非export的Activity);
由于Google Play的开发者条款限制,不建议在GP渠道动态更新代码;
在Android N上,补丁对应用启动时间有轻微的影响;
不支持部分三星android-21机型,加载补丁时会主动抛出"TinkerRuntimeException:checkDexInstall failed";
对于资源替换,不支持修改remoteView。例如transition动画,notification icon以及桌面图标。
在当前流行的热更新框架中tinker可以说是功能最为齐全的(
可以完成方法替换、类替换、类结构以及资源的修改、so替换、jar文件替换,可以完美支持全平台),最主要的是可以完美的结合gradle构建工具使用生成差异包。怎么样,我们仅仅说最后一条,你是不是就已经心动了。以为我们只要集成完成一次,剩下的事情就是简单的调一下参数这么简单了。
这里强调几点,我们选择开源框架的几个原则
- 看公司的项目需求,满足公司项目需求这是必要的一步,这里说的满足,并不一定要完全的超预期还要符合下面的几点要求。
- 尽量选择大厂并且项目有专门团队在维护,而且最近一直在维护。(因为不断的维护,这样可以减小入坑的机会同时受众广,框架会得到很好的验证)
- 尽量选择学习成本较低的一个。(这种一般选择文档清晰,使用受众广的出了问题方便排查)
tinker
是腾讯大厂的一个成熟的开源项目,在github上有自己的维护地址:https://github.com/Tencent/tinker 项目目前14k star 足可见还是让开发者普遍接受的。
6. AndroidStudio gradle构建脚本下的Tinker集成
6.1 集成tinker的环境要求
集成环境要求 Android studio 3.1.1
及其以上
6.2 Tinker 的集成步骤
6.2.1 在工程项目的根目录中的build.gradle添加 tinker-gradle-plugin
作为项目的依赖
classpath("com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}") {
changing = TINKER_VERSION?.endsWith("-SNAPSHOT")
exclude group: 'com.android.tools.build', module: 'gradle'
}
6.2.2 在项目moudle的 app/build.gradle 中,添加如下依赖
//方法过多google提供的分包解决方案 参考 https://yq.aliyun.com/articles/12429 和 http://blog.csdn.net/gulihui890411/article/details/48551551
implementation 'com.android.support:multidex:1.0.3'
//optional, help to generate the final application
compileOnly("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true }
//tinker's main Android lib
implementation("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}") { changing = true }
// Maven local cannot handle transist dependencies.
implementation("com.tencent.tinker:tinker-android-loader:${TINKER_VERSION}") { changing = true }
annotationProcessor("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") {
changing = true
}
6.2.3 在 gradle.properties
文件中配置 TINKER_VERSION
变量,统一指定依赖库和plugin的版本号
gradle.properties
文件中添加 TINKER_VERSION=1.9.1
,(gradle.properties
文件在根项目的 build.gradle
同级目录,没有同学请自行创建) ,这里我们以 1.9.1 版本为例。

注: 依赖库和plugin版本号不一致会导致编译出现异常,请慎重检查
6.2.4 同步一下项目工程
6.2.5 创建 CustomApplicationLike
继承 ApplicationLike
,编译后,Tinkerplugin会自动帮我们创建自定义名称的 MApplication
对象继成自 TinkerApplication
(这个Application相当于我们以前的自定义Application)
通过注解关键字
DefaultLifeCycle
指定参数,application 对应要生成的自定义 application 名称和位置(比如我们指定为MApplication ,同步一下代码就会在build文件夹下看到生成的MApplication
对象),默认是和CustomApplicationLike
在同一个包中,参数 flags 可以指定动态更新的范围,默认的取值
- 注解参数说明
取值 | 默认值 | 描述 | 备注 |
---|---|---|---|
application | null | gradle脚本生成自定义 application 的位置和名称 |
|
flags | TINKER_DISABLE | 更新增量包加载类型标识位 | tinker运行时支持的补丁包中的文件类型: 1. ShareConstants.TINKER_DISABLE:不支持任何类型的文件; 2. ShareConstants.TINKER_DEX_ONLY:只支持dex文件; 3. ShareConstants.TINKER_LIBRARY_ONLY:只支持library文件; 4. ShareConstants.TINKER_DEX_AND_LIBRARY:只支持dex与res的修改; 5. ShareConstants.TINKER_ENABLE_ALL:支持任何类型的文件,也是我们通常的设置的模式。 |
loadVerifyFlag | 更新增量包校验标识位 |
- flags 的取值和含义
参数名称 | 描述 | 备注 |
---|---|---|
TINKER_DISABLE | 不支持任何类型的文件 | |
TINKER_DEX_MASK | 只支持dex文件 | |
TINKER_NATIVE_LIBRARY_MASK | 只支持library文件 | |
TINKER_RESOURCE_MASK | 只支持resource文件 | |
TINKER_DEX_AND_LIBRARY | 只支持dex与res的修改 | |
TINKER_ENABLE_ALL | 支持任何类型的文件,也是我们通常的设置的模式 |
@DefaultLifeCycle(application = ".MApplication"
, flags = ShareConstants.TINKER_ENABLE_ALL
, loadVerifyFlag = false)
public class CustomApplicationLike extends ApplicationLike {
public static Context sApplication;
public static CustomApplicationLike sApplike;
public CustomApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
}
@Override
public void onBaseContextAttached(Context base) {
super.onBaseContextAttached(base);
//使应用支持分包
MultiDex.install(base);
SampleApplicationContext.application = getApplication();
SampleApplicationContext.context = getApplication();
TinkerManager.setTinkerApplicationLike(this);
TinkerManager.initFastCrashProtect();
//should set before tinker is installed
TinkerManager.setUpgradeRetryEnable(true);
//optional set logIml, or you can use default debug log
//TinkerInstaller.setLogIml(new MyLogImp());
//installTinker after load multiDex
//or you can put com.tencent.tinker.** to main dex
TinkerManager.installTinker(this);
Tinker tinker = Tinker.with(getApplication());
//获取全局上下文
sApplication = base;
sApplike = this;
CommonUtil.getInstance().init(base, new Config()
.setLogOpen(true)
.setLogTag("ZBC_TINKER")
.setToastOpen(true));
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
getApplication().registerActivityLifecycleCallbacks(callback);
}
}
更新后看到tinker为我们代码生成的
MApplication
类

6.2.6 需要在 AndroidManifest.xml
文件中配置这个Application的名称需要和 CustomApplicationLike
类的注解一直

6.2.7 改造项目原有的Appalication
这个分两种情况
-
我们的项目原来已经有自己的
MApplication
继承系统的Application
,这时候我们应该把application
中的代码移植到自己创建的SampleApplicationLike
类,这个类继承自ApplicationLike
,建议将例如:@DefaultLifeCycle(application = ".MApplication" //application类名 , flags = ShareConstants.TINKER_ENABLE_ALL //tinkerFlags , loadVerifyFlag = false) //tinkerLoadVerifyFlag public class CustomApplicationLike extends ApplicationLike { public static Context sApplication; public static CustomApplicationLike sApplike; public CustomApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMilli