Tinker是什么
Tinker是微信官方的Android热补丁解决方案,它支持动态下发代码、So库以及资源,让应用能够在不需要重新安装的情况下实现更新。当然,你也可以使用Tinker来更新你的插件。
为什么使用Tinker
当前市面的热补丁方案有很多,其中比较出名的有阿里的AndFix、美团的Robust以及QZone的超级补丁方案。但它们都存在无法解决的问题,这也是正是使用Tinker的原因。
总的来说:
1、AndFix作为native解决方案,首先面临的是稳定性与兼容性问题,更重要的是它无法实现类替换,它是需要大量额外的开发成本的;
2、Robust兼容性与成功率较高,但是它与AndFix一样,无法新增变量与类只能用做的bugFix方案;
3、Qzone方案可以做到发布产品功能,但是它主要问题是插桩带来Dalvik的性能问题,以及为了解决Art下内存地址问题而导致补丁包急速增大的。
Tinker的已知问题
由于原理与系统限制,Tinker有以下已知问题:
1、Tinker不支持修改AndroidManifest.xml,Tinker不支持新增四大组件;
2、由于Google Play的开发者条款限制,不建议在GP渠道动态更新代码;
3、在Android N上,补丁对应用启动时间有轻微的影响;
4、不支持部分三星android-21机型,加载补丁时会主动抛出"TinkerRuntimeException:checkDexInstall failed";
5、对于资源替换,不支持修改remoteView。例如transition动画,notification icon以及桌面图标。
Tinker集成
一、配置gradle
1,在项目的gradle.properties文件中添加Tinker的版本号
TINKER_VERSION=1.9.14.3
#TINKER_ID主要作用是区分当前打出的补丁包是基于哪个版本的apk,也就是说当前打出的补丁包能修复哪个版本的bug。这里可以填versionName
versionName=1.0.1
TINKER_ENABLE=true
android.enableD8.desugaring = true
android.useDexArchive = true
2,在项目的build.gradle中,添加tinker-patch-gradle-plugin的依赖
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.9.14.3')
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
3,在app的gradle文件app/build.gradle,添加tinker的库依赖
implementation 'com.android.support:multidex:1.0.3'
//optional, help to generate the final application
compileOnly('com.tencent.tinker:tinker-android-anno:1.9.14.3')
//tinker's main Android lib
implementation('com.tencent.tinker:tinker-android-lib:1.9.14.3')
4,在app的gradle文件app/build.gradle,添加tinker的相关配置
下面就把整个app/build.gradle配置提出来,其中Tinker相关配置抽取到另一个文件tinkerpatch.gradle,其中比较重要的属性都标有中文注释,其余属性解释可以自行参考 Tinker 接入指南
apply from: 'tinkerpatch.gradle'
apply plugin: 'com.tencent.tinker.patch'
//-----------------------tinker配置区-----------------------------
def bakPath = file("${buildDir}/bakApk/")
def baseInfo = "app-release-1018-17-55-04"//这里需要修改为build/bakApk下面比对的旧包名字。这里填对应发版的文件名,只会修复发这个版本之后的bug,以前发的其它版本不起作用
//def gitSha() {//该方法需要安装git,并将项目与git建立连接,本例中不使用git,故注释
// try {
// String gitRev = 'git rev-parse --short HEAD'.execute(null, project.rootDir).text.trim()
// if (gitRev == null) {
// throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'")
// }
// return gitRev
// } catch (Exception e) {
// throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'")
// }
//}
/**
* you can use assembleRelease to build you base apk
* use tinkerPatchRelease -POLD_APK= -PAPPLY_MAPPING= -PAPPLY_RESOURCE= to build patch
* add apk from the build/bakApk
*/
ext {
//开发者模式下,关闭插件
def sp = project.gradle.startParameter
def taskName = sp.taskNames[0]
def isopenthinker = true
if (taskName.equals(":app:assembleDebug")) {
isopenthinker = false
}
//for some reason, you may want to ignore tinkerBuild, such as instant run debug build?
tinkerEnabled = isopenthinker
//for normal build
//old apk file to build patch apk
tinkerOldApkPath = "${bakPath}/${baseInfo}.apk"
//proguard mapping file to build patch apk
tinkerApplyMappingPath = "${bakPath}/${baseInfo}-mapping.txt"
//resource R.txt to build patch apk, must input if there is resource changed
tinkerApplyResourcePath = "${bakPath}/${baseInfo}-R.txt"
//only use for build all flavor, if not, just ignore this field
tinkerBuildFlavorDirectory = "${bakPath}/app-1018-17-32-47"
}
def getOldApkPath() {
return hasProperty("OLD_APK") ? OLD_APK : ext.tinkerOldApkPath
}
def getApplyMappingPath() {
return hasProperty("APPLY_MAPPING") ? APPLY_MAPPING : ext.tinkerApplyMappingPath
}
def getApplyResourceMappingPath() {
return hasProperty("APPLY_RESOURCE") ? APPLY_RESOURCE : ext.tinkerApplyResourcePath
}
def getTinkerIdValue() {
// return hasProperty("TINKER_ID") ? TINKER_ID : gitSha()
return versionName //需要保证TINKER_ID有设置(在gradle.properties中)
}
def buildWithTinker() {
return hasProperty("TINKER_ENABLE") ? TINKER_ENABLE : ext.tinkerEnabled
}
def getTinkerBuildFlavorDirectory() {
return ext.tinkerBuildFlavorDirectory
}
if (buildWithTinker()) {
apply plugin: 'com.tencent.tinker.patch'
tinkerPatch {
/**
* necessary,default 'null'
* the old apk path, use to diff with the new apk to build
* add apk from the build/bakApk
* 必须,默认为null
* 基准apk包的路径
*/
oldApk = getOldApkPath()
/**
*
* optional,default 'false'
* there are some cases we may get some warnings
* if ignoreWarning is true, we would just assert the patch process
* case 1: minSdkVersion is below 14, but you are using dexMode with raw.
* it must be crash when load.
* case 2: newly added Android Component in