今天总结下近期调研的一款热修复框架——Tinker。为了方便快速集成和使用,我们选择了微信提供的快速集成平台TinkerPatch(官方网站)。
Tinker 是一个开源项目(Github链接),它是微信官方的 Android 热补丁解决方案,它支持动态下发代码、So 库以及资源,让应用能够在不需要重新安装的情况下实现更新。
一、添加Gradle插件依赖
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
// TinkerPatch 插件
classpath "com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:1.2.6"
}
}
二、集成SDK,在app的build.gralde中加入第三方依赖
dependencies {
...
// 若使用annotation需要单独引用,对于tinker的其他库都无需再引用
provided("com.tinkerpatch.tinker:tinker-android-anno:1.9.6")
compile("com.tinkerpatch.sdk:tinkerpatch-android-sdk:1.2.6")
}
三、配置 tinkerpatchSupport 参数
为了简单方便,我们将 TinkerPatch 相关的配置都放于 tinkerpatch.gradle 中, 我们需要将其引入:
apply from: 'tinkerpatch.gradle'
tinkerpatchSupport配置如下:
apply plugin: 'tinkerpatch-support'
/**
* TODO: 请按自己的需求修改为适应自己工程的参数
*/
def bakPath = file("${buildDir}/bakApk/")
def baseInfo = "app-1.0.0-0515-10-21-34" //基准包目录
def variantName = "release"
/**
* 对于插件各参数的详细解析请参考
* 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 = true
/**
* 是否开启加固模式,只能在APK将要进行加固时使用,否则会patch失败。
* 如果只在某个渠道使用了加固,可使用多flavors配置
**/
protectedApp = false
/**
* 实验功能
* 补丁是否支持新增 Activity (新增Activity的exported属性必须为false)
**/
supportComponent = true
autoBackupApkPath = "${bakPath}"
appKey = "95f17f5aaeb6334a"
/** 注意: 若发布新的全量包, appVersion一定要更新 **/
appVersion = "1.0.0"
def pathPrefix = "${bakPath}/${baseInfo}/${variantName}/"
// def name = "${project.name}-${variantName}"
def name = "app-release"
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-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
*/
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
}
}
一、初始化
public class SampleApplication extends Application {
private ApplicationLike tinkerApplicationLike;
@Override
public void onCreate() {
super.onCreate();
initTinkerPatch();
...
}
private void initTinkerPatch() {
// 我们可以从这里获得Tinker加载过程的信息
tinkerApplicationLike = TinkerPatchApplicationLike.getTinkerPatchApplicationLike();
// 初始化TinkerPatch SDK, 更多配置可参照API章节中的,初始化SDK
TinkerPatch.init(tinkerApplicationLike)
.reflectPatchLibrary()
.setPatchRollbackOnScreenOff(true)
.setPatchRestartOnSrceenOff(true)
.setFetchPatchIntervalByHours(3);
// 每隔3个小时(通过setFetchPatchIntervalByHours设置)去访问后台时候有更新,通过handler实现轮训的效果
TinkerPatch.with().fetchPatchUpdateAndPollWithInterval();
// 获取当前的补丁版本
LogUtils.d("Current patch version is " + TinkerPatch.with().getPatchVersion());
}
}
二、构建基准包
运行 assembleRelease task 构建基准包,tinkerPatch会基于你填入的autoBackupApkPath自动备份基础包信息到相应的文件夹,包含:apk文件、R.txt文件和mapping.txt文件 (注:mapping.txt是proguard的产物,如果你没有开启proguard则不会有这个文件)
将基准包生成出来的文件路径分别填到tinkerpatchSupport中的baseApkFile、baseProguardMappingFile和baseResourceRFile 参数中。
三、发布补丁包
进行一些的BUG修改后,运行 tinkerPatchRelease task 构建补丁包,补丁包将位于 build/outputs/apk/tinkerPatch下。
上传patch_signed.apk到tinkerpatch管理平台。
四、下载安装补丁
在初始化时默认设置了3小时轮询依次查询后台有无更新补丁,若需要手动立即更新补丁,则使用如下方法
TinkerPatch.with().fetchPatchUpdate(true);
补丁自动下载、自动合并,需要彻底关闭app后重启方可生效。这里提供一个杀掉进程的方法,可以在适当的时机调用
ShareTinkerInternals.killAllOtherProcess(getApplicationContext());
android.os.Process.killProcess(android.os.Process.myPid());
五、回滚补丁
sdk提供了一键回滚到基准包到方法
TinkerPatch.with().cleanAll();
一、补丁中如果有新增activity
需在基准包中的tinkerpatchSupport配置开启
supportComponent = true
且新activity的export属性必须false
二、在线参数
TinkerPlatform提供方便配置在线参数的功能,我们在代码中可以通过如下方法获取参数
TinkerPatch.with().fetchDynamicConfig(new ConfigRequestCallback() {
@Override
public void onSuccess(HashMap configs) {
TinkerLog.w(TAG, "request config success, config:" + configs);
Iterator> it = configs.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = it.next();
ToastUtils.showShort("key= " + entry.getKey() + " and value= " + entry.getValue());
}
}
@Override
public void onFail(Exception e) {
TinkerLog.w(TAG, "request config failed, exception:" + e);
}
}, true);
1. 编译后报如下错误:
Execution failed for task ':app:javaPreCompileDebug'.
> Annotation processors must be explicitly declared now. The following dependencies on the compile classpath are found to contain annotation processor. Please add them to the annotationProcessor configuration.
- tinker-android-anno-1.9.6.jar (com.tinkerpatch.tinker:tinker-android-anno:1.9.6)
解决方法:
在app的build中加入
android {
...
defaultConfig {
...
//添加如下配置就OK了
javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
}
...
}
2. Bad notification for startForeground
如果出现如下报错,请检查是否在AndroidManifest.xml配置了icon信息。
android.app.RemoteServiceException: Bad notification for startForeground: java.lang.RuntimeException: invalid service notification: Notification(pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x40 color=0x00000000 vis=PRIVATE)
### END ###