微信Tinker的使用-Android热修复方案

这类文章已经很多了,写这个是为了记录下自己的使用过程和在使用中遇到过的一些问题。
看过一句话,无论学习什么技术,以官方文档为主,教程文章为辅。所以认真看文档。

Tinker

介绍下Tinker,其实这些官方文档都有,写在这里也就是为了自己再看的时候方便。

Tinker是什么

Tinker是微信官方的Android热补丁解决方案,它支持动态下发代码So库以及资源,让应用能够在不需要重新安装的情况下实现更新。也可以使用Tinker来更新插件
主要包括以下几个部分:

  • gradle编译插件:tinker-patch-gradle-plugin
  • 核心sdk库:tinker-android-lib
  • 非gradle编译用户的命令行版本:tinker-patch-cli.jar

与其他方案比较

阿里的AndFix、美团的Robust以及QZone的超级补丁方案

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文档的内容,想具体了解其他方案还是到每个的官方文档进行了解AndFix、Robust。

已知问题

  • Tinker不支持修改AndroidManifest.xml,Tinker不支持新增四大组件(1.9.0支持新增非export的Activity);
  • 由于Google Play的开发者条款限制,不建议在GP渠道动态更新代码;
  • 在Android N上,补丁对应用启动时间有轻微的影响;
  • 不支持部分三星android-21机型,加载补丁时会主动抛出"TinkerRuntimeException:checkDexInstall failed";
  • 对于资源替换,不支持修改remoteView。例如transition动画,notification icon以及桌面图标

开始接入

官方接入文档

gradle接入

在项目的build.gradle文件中,添加tinker-patch-gradle-plugin的依赖

dependencies{
      classpath "com.tencent.tinker:tinker-patch-gradle-plugin:x.x.x"
}

在app的build.gradle文件中,添加tinker的库依赖以及apply tinker的gradle插件.

dependencies{ 
      //可选,用于生成application类 
      provided('com.tencent.tinker:tinker-android-anno:x.x.x')
      //tinker的核心库
      compile('com.tencent.tinker:tinker-android-lib:x.x.x') 
}
...
...
//apply tinker插件
apply plugin: 'com.tencent.tinker.patch'

这三个依赖在添加的时候版本号必须保持一致。

配置gradle中的参数

我自己在build.gradle中参数的配置基本是按照demo中build.gradle配置的,或者也可以查看文档中的参数说明,弄清楚每个参数代表什么以后,根据自己的需求进行配置。

修改Application类

这里有两种方法进行修改,一种是将自己的Application类继承TinkerApplication.Java;第二种是使用Annotation来自动生成Application类。官方推荐是使用第二种方式,Annotation自动生成Application类,既然已经用了Tinker方案了,就按官方推荐的接入。文档中自定义Application类。

要用Annotation来自动生成Application类的原因是:程序启动时会加载默认的Application类,这导致补丁包无法对其做修改。
具体实现:
1、新建一个自己的ApplicationLike类,让其继承DefaultApplicationLike,并添加相关注释

@DefaultLifeCycle(
        application = ".MyApplication",    //application类名
        flag = ShareConstants.TINKER_ENABLE_ALL,   //tinkerFlag
        loaderClass = "com.tencent.tinker.loader.TinkerLoader",   //loaderClassName, 我们这里使用默认即可!
        loadVerifyFlag = false
)
public class MyApplicationLike extends DefaultApplicationLike{
      ...
}

2、将原先自己的Application类以及他的继承类的所有代码拷贝到新建的自己的ApplicationLike中。
3、原先Application的attachBaseContext方法实现要单独移到onBaseContextAttached中。
4、在现在自己的ApplicationLike中,所有引用到application的地方改成getApplication()。
5、对其他引用原先Application或者它的静态对象和方法的地方,改成引用现在自己的ApplicationLike的静态对象和方法。

通过Annotation方式生成Application类,需要将原来自己的Application类删除,其他任何类都不能再引用原来自己的Application类。

修改好Application以后,在AndroidManifest.xml中,修改application的name属性,与MyApplicationLike注释中写的保持一致。之后在自己的ApplicationLike中的onBaseContextAttached()方法中,通过TinkerInstaller.install()来初始化Tinker。到此为止,Tinker的接入已初步完成,接下来可以实现补丁功能。

实现补丁功能

将基准包安装到手机,修改需要更新的内容后,将基准包和相应文件的路径填写在build.gradle中的对应位置上,调用tinkerPatch Task进行编译,生成补丁包,将补丁包放在手机的sdcard中,用基准包安装在手机上的应用加载补丁,加载成功后重启,补丁生效,更新成功。
官方demo使用方法

生成补丁包

在成功接入tinker并且build完成以后,在Android Studio的工程目录中会生成bakApk文件夹,路径对应自己在gradle中的设置。


微信Tinker的使用-Android热修复方案_第1张图片
image

在bakApk文件夹下的内容,就是每次在生成补丁之前,在gradle中进行配置时所需要的XXX.apk(基准包)、XXX_mapping.txt(打开混淆以后才会产生)、XXX_R.txt文件。

同时,会在Android Studio的Gradle工具栏中会生成tinkerPatch Task文件,直接使用task:tinkerPatchVariantName(例如tinkerPatchDebug、tinkerPatchRelease)即可自动根据Variant选择相应的编译类型。


微信Tinker的使用-Android热修复方案_第2张图片
图片.png

微信Tinker的使用-Android热修复方案_第3张图片
图片.png

在编译过程中,还帮助我们完成了以下操作:

  • 将TINKER_ID自动插入AndroidManifest的meta项,输出路径为build/intermediates/tinker_intermediates/AndroidManifest.xml;

  • 如果minifyEnabled为true即打开混淆,将自动将Tinker的proguard规则添加到proguardFiles中,输出路径为build/intermediates/tinker_intermediates/tinker_proguard.pro,这里不需要将它们拷贝到自己的proguard配置文件中;

  • 如果multiDexEnabled为true,将自动生成Tinker需要放在主dex的keep规则。在tinker 1.7.6版本之前,你需要手动将生成规则拷贝到自己的multiDexKeepProguard文件中。例如Sample中的multiDexKeepProguard file("keep_in_main_dex.txt")。在1.7.6版本之后,这里会通过脚本自动处理,无须手动填写。

  • 把dexOptions的jumboMode打开。

在进行tinkerPatch Task编译之前,需要将bakApk文件夹下每个文件路径填写在对应的位置上。多flavor打包。

//demo中的配置将相关属性写在一起,方便每次更改
ext {
    //demo中的这个属性表示是否打开tinkerBuild
    tinkerEnabled = true
    //基准包apk的位置
    tinkerOldApkPath = "${bakPath}/"
    //打开混淆生成的mapping.txt的路径
    tinkerApplyMappingPath = "${bakPath}/"
    //R.txt文件的路径
    tinkerApplyResourcePath = "${bakPath}/"
    //用了flavor以后在这里填写编译后的目录路径,没用flavor可以不填
    tinkerBuildFlavorDirectory = "${bakPath}/"
}

填写完成以后直接使用tinkerPatch Task中相应的操作,即自动编译最新的安装包,并与输入基准包作差异,得到最终的补丁包。,编译完成后,会在tinkerPatch输出的目录build/outputs/tinkerPatch中产生我们需要的文件。

文件名 描述
patch_unsigned.apk 没有签名的补丁包
patch_signed.apk 签名后的补丁包
patch_signed_7zip.apk 签名后并使用7zip压缩的补丁包,也是我们通常使用的补丁包。但正式发布的时候,最好不要以.apk结尾,防止被运营商挟持。
log.txt 在编译补丁包过程的控制台日志
dex_log.txt 在编译补丁包过程关于dex的日志
so_log.txt 在编译补丁包过程关于lib的日志
tinker_result 最终在补丁包的内容,包括diff的dex、lib以及assets下面的meta文件
resources_out.zip 最终在手机上合成的全量资源apk,你可以在这里查看是否有文件遗漏
tempPatchedDexes 在Dalvik与Art平台,最终在手机上合成的完整Dex,我们可以在这里查看dex合成的产物。

官方提示:每次编译结束,我们都应该查看相关日志,清楚最终在补丁包中的文件。尤其是dex的补丁文件,即使是1k的dex补丁文件,也会带来合成时的时间损耗以及合成完整dex文件ROM空间体积这两部分影响!

加载补丁包

将基准包安装到手机,再将生成好的patch_signed_7zip.apk补丁包放到手机的sdcard中,正式使用时从服务器下载补丁包。然后在需要加载补丁包的位置调用TinkerInstaller.onReceiveUpgradePatch加载对应路径补丁包。

需要注意:在Tinker的更新使用中,默认在DefaultTinkerResultService会杀掉:patch进程,假设当前是补丁升级并且成功了,我们会杀掉当前进程,让补丁包更快的生效(这就是为什么有的人接好Tinker,实现了更新功能,但是每次加载完补丁会Crash的原因)。若是修复类型的补丁包并且失败了,我们会卸载补丁包。

扩展

Tinker中还有很多可选自定义类。可以通过继承默认的类,实现自己感兴趣的事件回调,比如在上面提到的,tinker在DefaultTinkerResultService中默认在加载补丁成功后杀死当前进程,这在正式的使用中会很降低用户体验,所以我们需要继承这个类,实现自己的回调。具体实现可以参考官方说明配合demo。

  • 自定义LoadReporter类
  • 自定义PatchReporter类
  • 自定义PatchListener类
  • 自定义AbstractResultService类
  • 自定义TinkerLoader类

你可能感兴趣的:(微信Tinker的使用-Android热修复方案)