微信Android热补丁方案Tinker初步使用
本文根据官网文档进行了一次初步使用,从APP的构建到补丁发布完整地介绍了Tinker,用来了解Tinker的使用,参考了官方Demo:
为什么使用Tinker
由于其他热补丁方案还未使用过,不好做出评论,以下引述Tinker官网上的说明:来说明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-7.X的全平台支持。利用Tinker我们不仅可以用做bugfix,甚至可以替代功能的发布。Tinker已运行在微信的数亿Android设备上,那么为什么你不使用Tinker呢?
下面我们来开始介绍 Tinker 的具体使用,包括以下几个部分:
- 集成 Tinker 及初始化
- 构建基准包及生成补丁文件
- Tinker 平台使用及发布补丁
一、集成 Tinker 及初始化
添加 gradle 插件依赖
buildscript {
repositories {
jcenter()
}
dependencies {
// TinkerPatch 插件
classpath "com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:1.1.4"
}
}
集成 TinkerPatch SDK
dependencies {
// 若使用annotation需要单独引用,对于tinker的其他库都无需再引用
provided("com.tencent.tinker:tinker-android-anno:1.7.7")
compile("com.tinkerpatch.sdk:tinkerpatch-android-sdk:1.1.4")
}
添加 TinkerPatch 配置
为了简单方便,Demo将 TinkerPatch 相关的配置都放于 tinkerpatch.gradle 中,
apply plugin: 'tinkerpatch-support'
/**
* TODO: 请按自己的需求修改为适应自己工程的参数
*/
def bakPath = file("${buildDir}/bakApk/")
def baseInfo = "app-1.0.0-0314-11-30-49"
def variantName = "debug"
import java.util.regex.Matcher
import java.util.regex.Pattern
/**
* 对于插件各参数的详细解析请参考
* http://tinkerpatch.com/Docs/SDK
*/
tinkerpatchSupport {
/** 可以在debug的时候关闭 tinkerPatch, isRelease() 可以判断BuildType是否为Release **/
tinkerEnable = true
reflectApplication = true
autoBackupApkPath = "${bakPath}"
/** 需要改为对应的appKey,下文新增APP里说明 **/
appKey = "5a25479b9aeb2e5e"
/** 注意: 若发布新的全量包, appVersion一定要更新 **/
appVersion = "1.0.0"
def pathPrefix = "${bakPath}/${baseInfo}/${variantName}/"
def name = "${project.name}-${variantName}"
/** 基准APK文件 **/
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}"
}
}
在 app/build.gradle 中引入 tinkerpatch.gradle :
apply from: 'tinkerpatch.gradle'
初始化 TinkerPatch SDK
public class MyApplication extends Application {
ApplicationLike tinkerAppLike;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(base);
}
@Override
public void onCreate() {
super.onCreate();
if (BuildConfig.TINKER_ENABLE){
tinkerAppLike = TinkerPatchApplicationLike.getTinkerPatchApplicationLike();
TinkerPatch.init(tinkerAppLike)
//是否自动反射Library路径,无须手动加载补丁中的So文件
//注意,调用在反射接口之后才能生效,你也可以使用Tinker的方式加载Library
.reflectPatchLibrary()
//设置收到后台回退要求时,锁屏清除补丁
//默认是等主进程重启时自动清除
.setPatchRollbackOnScreenOff(true)
//设置补丁合成成功后,锁屏重启程序
//默认是等应用自然重启
.setPatchRestartOnSrceenOff(true);
}
// 每隔一小时检查一次,按自己需求设定
new FetchPatchHandler().fetchPatchWithInterval(1);
}
}
...
最后在AndroidManifest.xml中设置MyApplication:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:name=".MyApplication"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
至此Demo已经构建完成,下面就开始进行补丁包的构建与发布。
二、构建基准包及生成补丁文件
首先直接运行task assembleRelease,此时即可在build文件夹内出现bakApk文件夹,里面存放的是每次编译的APK文件,也就是完整的APP安装包。
基准包文件并且我们可以看到在build/bakApk文件夹内已经生成了三个文件:
build/bakApk文件夹
执行assembleDebug 没有mapping文件。
生成补丁文件
现在需要生成上面的基准包APK的补丁文件。作为测试Demo我们这里随便改动TextView文字
<!--Hello World 改为 Hello World!!!!!!!!!!!-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!!!!!!!!!!!" />
改好代码后,我们开始生成补丁包:
这里需要我们在tinkerpatch.gradle文件中配置三个文件路径:
- baseApkFile:基准APK文件
- baseProguardMappingFile:基准包的 Proguard mapping.txt 文件路径
- baseResourceRFile:基准包的资源 R.txt 文件路径
而在官方的tinkerpatch.gradle文件中已经帮我们配置好基本路径:
baseApkFile = "${pathPrefix}/${name}.apk"
// 这里tinkerPatchDubug和tinkerPatchRelease不同的是tinkerPatchRelease需要配置mapping文件,否则task会失败
baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt"
baseResourceRFile = "${pathPrefix}/${name}-R.txt"
我们只需要将tinkerpatch.gradle文件中baseInfo = "app-1.0.0-0314-11-30-49"值改成要生成补丁的基准包所在文件夹名称即可(即上文bakApk文件夹下基准包文件夹名称,选择哪个文件夹生成的补丁就是基于对应的基准包)。
接下来就执行tinkerPatchRelease task开始生成补丁文件了:
tinkerPatchRelease task需要耐心等待一会,执行结束会在outputs文件夹内生成tinkerPatch文件夹:
该文件夹内文件还是挺多的,
patch_signed_7zip.apk
既是我们要的补丁包。
三、Tinker 平台使用及发布补丁
首先登录Tinker 平台注册账号,然后需要经过3个步骤即可完成补丁包的发布:
- 新增APP
- 添加版本
- 发布补丁
新增APP
新增APP若首次使用点击新增APP
后,输入APP的名称,名称可以再次修改。此时会生成对应的appKey(如下图左侧),需要将key填入到 tinkerpatch.gradle 中: appKey = "5a25479b9aeb2e5e"
。
添加版本
添加版本输入需要打补丁的基准APP的版本号,不同的版本因为迭代的原因补丁也不相同,需要前期做好规划。
发布补丁
发布补丁需要对应APP版本对应的补丁文件,既是我们上文中提到的
patch_signed_7zip.apk
文件。
测试结果
经过了大约20分钟看到了测试机补丁merge成功,没有立即看到效果的可以等等:
Hello World 已被改为 Hello World!!!!!!!!!!!补丁详情里可以查看该补丁的信息:
补丁详情
平台也提供了实时的数据监控,方便及时了解补丁的安装情况:
实时监控
Tinker的已知问题
由于原理与系统限制,Tinker有以下已知问题:
- Tinker不支持修改AndroidManifest.xml,Tinker不支持新增四大组件;
- 由于Google Play的开发者条款限制,不建议在GP渠道动态更新代码;
- 在Android N上,补丁对应用启动时间有轻微的影响;
- 不支持部分三星android-21机型,加载补丁时会主动抛出"TinkerRuntimeException:checkDexInstall failed";
- 由于各个厂商的加固实现并不一致,在1.7.6以及之后的版本,tinker不再支持加固的动态更新;
- 对于资源替换,不支持修改remoteView。例如transition动画,notification icon以及桌面图标。
以上就是一个Tinker完整的使用过程,并没有涉及到原理及深入使用,希望对大家有所帮助!
本文的Demo