什么是热修复(热更新,热补丁)?
一句话概括,动态地修复和更新App的行为
热修复有什么好处?
用户下载了我们的app,如果出现bug或者我们要修改某个功能,我们就要重新编译一个版本让用户覆盖安装才能解决问题,不过这份成本非常高,我们首先要发一个版本,其次用户还得重新安装下载apk,这个过程可能会使用户流失掉,热修复,可以在用户神不知鬼不觉的情况下就完成bug的修复和新功能的实现
有了热修复就可以一劳永逸吗?
目前市面上的所有热修复框架都不支持四大组件的生成和AndroidManifest文件的编写,如果有重大版本更新或者新页面,还是需要走正常发布流程。而bug的修复和一些资源的替换,才可以使用热修复技术。发布正式版本和发布热修复版本同样需要经过测试,因为各种热修复技术都会有兼容性的问题。
接入准备工作
- 了解android dex文件和classLoader原理(热修复技术的底层核心实现,自行google了解)
- 了解当前市面上比较流行的几种热修复技术以及技术选型
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 |
复杂度 | 较低 | 较低 | 复杂 | 复杂 |
Rom体积 | Dalvik较大 | 较小 | 较小 | 较小 |
成功率 | 较高 | 较高 | 一般 | 最高 |
因为AndFix https://github.com/alibaba/AndFix 已经三年多没更新,所以我们首先排除这个,然后因为我们经常要用到字符串更改之类的需求,Robust没有资源替换,所以排除这个,剩下Tinker和QZone,我们对比发现Tinker性能损耗和补丁包大小都比较小,所以我们选择Tinker为我们的热修复基础。
方案对比以及技术选型需要主要的地方
- 1.我们的需求是什么,需求是衡量一切的标准。
- 2.满足需求的情况下哪个学习成本最低。
- 3.学习成本一样的情况下,优先选择大公司的方案)。
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以及桌面图标。
什么是TinkerPatch
Tinker 需要使用者有一个后台可以下发和管理补丁包,并且需要处理传输安全等部署工作,TinkerPatch 平台帮你做了这些工作,提供了补丁后台托管,版本管理,保证传输安全等功能,让你无需搭建一个后台,无需关心部署操作,只需引入一个 SDK 即可立即使用 Tinker。
此外,通过深入研究 Tinker 源码,TinkerTinkerPatch 平台在 Tinker的基础上加入了以下特性:
- 一键傻瓜式接入;无需理解复杂的热修复原理,一行代码即可接入热修复。实现了自动反射 Appliction 与 Library,使用者无需对自己的项目做任何的改动;
- 补丁管理;实现了热补丁的版本管理,补丁的自动重试与异常时自动回退等功能。同时我们可以简单实现条件下发补丁,在出现异常情况时,我们也可以快速回滚补丁;
- 编译优化;简化了 Tinker 的编译复杂度,实现了备份路径选择,功能开关等功能。
如何接入TinkerPatch
- 添加 gradle 插件依赖
- 在项目根目录的gradle.properties(没有则新建一个)填写以下代码。目的是方便管理Tinker和TinkerPatch的版本。
1 | TINKER_VERSION=1.9.8 |
- 在项目根目录的build.gradle中加入
1
2
3
4
5
6
7
8
9
10buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.0'
classpath "com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:${TINKERPATCH_VERSION}"
}
}
- 集成 TinkerPatch SDK,在app moudle下的build.gradle添加以下依赖。注意在com.android.tools.build:gradle 3.0 以下版本implementation改为compile,而compileOnly改为provided等…
1
2
3
4
5
6
7
8//若使用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
} - TinkerPatch 相关的配置,我们把TinkerPatch的配置全部写在一个文件中,新建一个文件名为tinkerpatch.gradle (和app moudle 中 build.gradle同级)
1 | apply plugin: 'tinkerpatch-support' |
具体含义可以参考 http://www.tinkerpatch.com/Docs/SDK
- 新建一个SampleApplication,并配置在AndroidManifest文件中
1 | public class SampleApplication extends Application { |
5.在程序入口Activity的onCreate()方法中执行加载补丁方法
1 | @Override |
使用步骤
1.构建基准包
方法一:在AS Terminal 命令窗口 执行gradlew assemblerelease 命令
方法二:在AS gradle执行assemblerelease命令行,如下图
2.把基准包信息填入tinkerpatch.gradle中
1 | def bakPath = file("${buildDir}/bakApk/") |
3.复制保存基准包
此时此基准包将作为正式服务器版本发布出去。需要保管妥当,将bakApk中对应的版本复制一份保存在本地,以免执行AS中Clean Project任务时删掉。如下图,
将app-1.0.0-0801-14-13-17整个文件夹复制保存于本地。如果在构建补丁包的时候没有找到的话,就把本地的copy进去。
4.构建补丁包
在线上的APP出现bug或者需要进行资源替换,就要生产补丁包,方法和构建基准包方法一样,有两种:
方法一:在AS Terminal 命令窗口 执行gradlew tinkerPatchRelease 命令
方法二:在AS gradle执行tinkerPatchRelease命令行,如下图
补丁包将位于 build/outputs/tinkerPatch下。如下图