微信Tinker 热修复介绍及接入(一)
声明:本文为博主原创文章,转载请注明出处:小嵩的博客
本系列传送门:
微信Tinker 热修复介绍及接入(一)
Tinker 原理深入理解(二)
Tinker 合并及加载补丁过程源码分析 (三)
什么是热修复?
定义 : 热修复(HotFix)是以补丁的方式动态修复紧急Bug,不再需要重新发布App,不需要用户重新下载覆盖安装的方式来实现代码的替换修改。这里就不多啰嗦了,可以自行搜索网上的介绍。
目前主流HotFix方案对比:
HotFix方案 | 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较大 | 较小 | 较小 | 较小 |
成功率 | 较高(95%) | 较高(96%) | 一般 | 最高(99.9%) |
注:
- Tinker的成功率数据,是从微信团队张绍文同学那儿打听得到的,该数据是微信APP自身的成功率,可信度高;
- Robust的成功率数据,来自美团Robust开源项目官方文档。
- QZone成功率和Tinker在同一水平的样子。
- AndFix 是公司以前就接入的,内部测试成功率只有80%左右(仅供参
考),而且修复起来还有诸多限制。
Tinker的原理
Tinker的优势和特性
综合考虑来说,Tinker的补丁包以及功能全面性、稳定性是比较吸引人的,并且功能还能做到类替换 、资源替换以及So替换。这样一来它就不仅仅是热修复了,还能做到热更新。因此我们最后采用了Tinker (其实还是因为微信几亿设备也是用的Tinker这套方案,靠谱点)。
微信和阿里还提供了补丁后台托管,版本管理SDK ,不缺钱或者不想因为热修复对项目代码造成侵入性的话,也可以直接使用微信或阿里封装好的傻瓜式接入方案,微信 Tinker Patch 方案目前是补丁包日请求量1w以内免费;阿里云 Sophix 目前还在公测阶段,暂时不收费。
微信 Tinker Patch 官方地址:Tinker Patch
阿里 SopHix 官方地址:Sophix
接入Tinker步骤
基于1.7.11版本,最新版可参考 Tinker GitHub 项目主页。
1.添加工程gradle plugin依赖
在项目的build.gradle中,添加tinker-patch-gradle-plugin的依赖
buildscript {dependencies {classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.7.11')}
}
2.添加tinker库依赖及插件应用
在app的gradle文件app/build.gradle,我们需要添加tinker的库依赖以及apply tinker的gradle插件:
//apply tinker插件
apply plugin: 'com.tencent.tinker.patch'
...
...
dependencies {//可选,用于生成application类 provided('com.tencent.tinker:tinker-android-anno:1.7.11')//tinker的核心库compile('com.tencent.tinker:tinker-android-lib:1.7.11')
}
3.gradle配置Tinker的一些参数
这步可参考Tinker 开源项目 sample中的app/build.gradle。
4.自定义Application代理类
程序启动时会加载默认的Application类,这导致我们补丁包是无法对它做修改了。如何规避?在这里我们并没有使用类似InstantRun hook Application的方式,而是通过代码框架的方式来避免,这也是为了尽量少的去反射,提升框架的兼容性。
这里我们要实现的是完全将原来的Application类隔离起来,即其他任何类都不能再引用我们自己的Application。将代码都放到代理类ApplicationLike中来,我们需要做的其实是以下几个工作:
- 将我们项目原来的Application类以及它的Base类的所有代码拷贝到创建的ApplicationLike继承类中,例如SampleApplicationLike。你也可以直接将自己的Application改为继承ApplicationLike,然后做改动;
- Application的attachBaseContext方法实现要单独移动到onBaseContextAttached中;
- 对ApplicationLike中,引用application的地方改成getApplication();
- 对其他引用Application或者它的静态对象与方法的地方,改成引用ApplicationLike的静态对象与方法;
更详细的内容大家可以参考sample例子里SampleApplicationLike的做法。
GitHub地址: tinker/tinker-sample-android/app/build.gradle
对于为何放弃Instant Run 实现,而采用代理的方案,张绍文同学是这么解释的:
详情可参考微信Android团队技术分享博客,地址链接:WeMobileDev/article
5.Tinker SDK初始化以及调用
初始化
创建一个类继承自ApplicationLike ,并添加DefaultLifeCycle注解,指定需要自动生成的Application路径和名称,将AndroidManifest.xml里面的application名称设置为它 :
<applicationandroid:name=".app.SampleApplication"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme">
代理类SampleApplicationLike 代码:
@SuppressWarnings("unused")
@DefaultLifeCycle(application = "tinker.sample.android.app.SampleApplication",flags = ShareConstants.TINKER_ENABLE_ALL,loadVerifyFlag = false)
public class SampleApplicationLike extends ApplicationLike {private static final String TAG = "Tinker.SampleApplicationLike";public SampleApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag,long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);}/*** install multiDex before install tinker* so we don't need to put the tinker lib classes in the main dex** @param base*/@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)@Overridepublic void onBaseContextAttached(Context base) {super.onBaseContextAttached(base);//you must install multiDex whatever tinker is installed!MultiDex.install(base);SampleApplicationContext.application = getApplication();SampleApplicationContext.context = getApplication();TinkerManager.setTinkerApplicationLike(this);TinkerManager.initFastCrashProtect();//should set before tinker is installedTinkerManager.setUpgradeRetryEnable(true);//optional set logIml, or you can use default debug logTinkerInstaller.setLogIml(new MyLogImp());//installTinker after load multiDex//or you can put com.tencent.tinker.** to main dexTinkerManager.installTinker(this);Tinker.with(getApplication());//初始化热更新SDK}@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {getApplication().registerActivityLifecycleCallbacks(callback);}}
写好之后Sync一下,它会在编译时自动生成SampleApplication。如果不想通过注解自动生成,我们也可以手动写这个Application放到项目里,但构造方法需要设置好代理类的path:
package tinker.sample.android.app;import com.tencent.tinker.loader.app.TinkerApplication;public class SampleApplication extends TinkerApplication {public SampleApplication() {super(7, "tinker.sample.android.app.SampleApplicationLike", "com.tencent.tinker.loader.TinkerLoader", false);}}
调用Tinker合并与清除补丁:
loadPatch :
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk");
loadLibrary :
// #method 1, hack classloader library pathTinkerLoadLibrary.installNavitveLibraryABI(getApplicationContext(), "armeabi");System.loadLibrary("stlport_shared");// #method 2, for lib/armeabi, just use TinkerInstaller.loadLibrary
// TinkerLoadLibrary.loadArmLibrary(getApplicationContext(), "stlport_shared");// #method 3, load tinker patch library directly
// TinkerInstaller.loadLibraryFromTinker(getApplicationContext(), "assets/x86", "stlport_shared");
cleanPatch:
Tinker.with(getApplicationContext()).cleanPatch();
6.补丁包生成与安装
6.1 打开右上侧Gradle,并双击assembleDebug,生成基准包。
6.2 安装基准包
app/build/bakApk 下,可以看到生成了基准包Apk以及R文件、mapping(mapping文件混淆下才会有),然后将该Apk安装到手机中。
平时开发测试时我们可通过AS 开发工具下方的Terminal 窗口 输入如下命令将APK Push到手机:
//APK已安装情况
adb install -r app/build/bakApk/app-debug-0620-14-12-54.apk
//APK未安装
adb install app/build/bakApk/app-debug-0620-14-12-54.apk
然后将app/build/bakApk 下生成的文件路径填入gradle 的ext 中:
ext {//for some reason, you may want to ignore tinkerBuild, such as instant run debug build?tinkerEnabled = true//for normal build//old apk file to build patch apktinkerOldApkPath = "${bakPath}/app-debug-0620-14-12-54.apk"//proguard mapping file to build patch apktinkerApplyMappingPath = "${bakPath}/app-debug-1018-17-32-47-mapping.txt"//resource R.txt to build patch apk, must input if there is resource changedtinkerApplyResourcePath = "${bakPath}/app-debug-0620-14-12-54-R.txt"//only use for build all flavor, if not, just ignore this fieldtinkerBuildFlavorDirectory = "${bakPath}/app-1018-17-32-47"
}
6.3 生成补丁包
oldApk路径填好之后,开始修改Bug,bug改完之后,双击tinkerPatchDebug,这个gradle命令会对当前代码和oldApk进行差异对比,在app/build/output/tinkerPatch下生成补丁。
生成的补丁信息,我们需要的补丁包是patch_signed_7zip.apk:
6.4 补丁包下载安装
补丁包生成之后,我们则可把它放到服务器后台,客户端通过接口去下载补丁包了,测试中我们一样是通过adb 将文件push到手机sd卡根目录:
adb push ./aipai/build/outputs/tinkerPatch/offical/debug/patch_signed_7zip.apk /storage/sdcard0/
补丁包push到手机之后,我们在基准包代码中已经写了如下代码,此时返回基准包触发该代码,则可把补丁包合并到基准包实现热更新:
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk");
爬坑及小技巧:
1.TinkerId 设置问题。
git项目中会有TinkerId,如果是通过非Clone方式拉取的代码,则需要push一次同步到Git中才会有,如果为了测试方便,也可以直接在 gtadle.properties文件指定tinkerId,如:TINKER_ID = 1
2.Java1.8 兼容问题
在gradle中设置 JavaVersion 为1.8,导致Application代理失败造成一启动就崩溃问题,有两种办法:
- 去除gradle tinker-android-anno 依赖库,不通过DefaultLifeCycle注解自动生成Application的办法,采用直接手动创建Application,并在构造方法中(第二个参数),设置代理类。
- anno 注解不支持 jackOptions 因此需要通过添加 lambda插件来兼容Java1.8
//添加插件
apply plugin: ‘me.tatarka.retrolambda’
3.补丁包push到sd卡:
adb push ./app/build/outputs/tinkerPatch/debug/patch_signed_7zip.apk /storage/sdcard0/
4.安装apk:
adb install app/build/bakApk/app-debug-0620-14-12-54.apk
或
adb install -r app/build/bakApk/app-debug-0620-14-12-54.apk
5.多渠道打包:
通过flavor 生成渠道包的情况下,会因为BuildInfo不同而导致Apk的Dex文件不同,从而导致每个渠道的补丁包都需要一对一,那么假如有几十个渠道,则同样需要几十个渠道的补丁包,这是非常不合理的。那么怎么办呢?
解决方案:
1.将渠道信息写在AndroidManifest.xml或文件中,例如channel.ini;
2.将渠道信息写在apk文件的zip comment中,这样一来,所有渠道包的Dex文件都是相同的,我们就可以通过assembleRelease 生成的基准包,来打补丁包。所有渠道都可以共用这个补丁包。至于这种渠道打包方式的工具,可以使用GitHub上开源的 packer-ng-plugin 或者可使用美团点评使用了V2 Scheme签名的 walle;
3.若不同渠道存在功能上的差异,建议将差异部分放于单独的dex或采用相同代码不同配置方式实现;
强烈建议采取第二种方式!!!
未完待续~
接下来这篇文章主要讲解一下Tinker的实现原理:Tinker原理深入理解(二)
欢迎交流讨论,有问题也非常欢迎指出不足之处~
微信Tinker 热修复介绍及接入(一)相关推荐
- 微信tinker 热修复
Tinker 是微信官方的Android热补丁解决方案,它支持动态下发代码.So库以及资源,让应用能够在不需要重新安装的情况下实现更新.当然,你也可以使用Tinker来更新你的插件. github:h ...
- Tinker热修复初探
听说热修复已经很久了,但这是第一次尝试去应用它.所以我对其它各种热修复也没什么了解,这里仅仅记下如何使用Tinker热修复. 对于Tinker热修复的介绍和问题这里也不写了,因为官方文档已经有了,戳这 ...
- 【错误记录】集成 Tinker 热修复报错 ( No such property: variantConfiguration for class: .ApplicationVariantData )
文章目录 一.报错信息 二.解决方案 一.报错信息 接入 Tinker 热修复 , 使用如下 Gradle 插件 , // Tinker 的 tinker-patch-gradle-plugin 插件 ...
- android Tinker 热修复 乐固加固后友盟打多渠道包之后的补丁失效
继上一篇 android tinker 热修复使用及注意事项 生成了热修复的补丁; 现在的需求是这样的,我想把这个包用腾讯乐固加固,然后生成多渠道包,希望这个补丁能修复所有这些渠道的包,经过测试,直 ...
- 腾讯Tinker 热修复 Andriod studio 3.0 配置和集成(二)多渠道打包和补丁发布
腾讯Tinker 热修复 Andriod studio 3.0 多渠道打包和发布补丁方式推荐 本文说明 在之前我已经分享了Tinker 热修复的 Andriod studio3.0 初次配置和集成,时 ...
- (十四)Tinker 热修复原理及手写实现
版权声明:本文为博主原创文章,未经博主允许不得转载. 本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下. 一.热修复 热补丁方案有很多,其中比较出名的有阿里的 AndFix.美团的 ...
- 【错误记录】Tinker 热修复示例运行报错 ( patch receive fail: /storage/emulated/0/patch_signed_7zip.apk, code: -2)
文章目录 一.报错信息 二.解决方案 参考 [Android 热修复]运行 Tinker 官方示例 博客 ; 一.报错信息 Tinker 热修复中 , 将生成的 patch 包 app-debug-p ...
- tinker热修复gradle接入
本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 2021-04-29更新,最近在复习热修复,发现我的老代码不行了,所以把这篇文章更新一下 本篇是gradle接入 tinker的gi ...
- 腾讯Tinker 热修复 Andriod studio 3.0 配置和集成(一)
本文说明 面试的时候经常问我有没有用过热修复?用谁的?能说下原理吗?当时我回答得不好,毕竟以前的项目都没有用,又不敢装逼,mmp,但是基本流程还是知道的,所以我们来初探下Tinker 这个热修复,如果 ...
- Tinker 热修复框架模拟使用
导言 前不久,腾讯推出了"微信小程序"这一概念,对移动原生应用的影响可谓巨大.而几乎就在同时, 腾讯在GitHub上开源了第一个项目Tinker, 这是一个Android平台的应用 ...
最新文章
- java hello work_Java入门教程系列 – 第一个程序 “hello, world”
- HH SaaS电商系统的供应商系统设计
- 使用js简单实现javaMap
- 在网页中放入贴纸插画是怎样的体验?这样的UI素材,你还不收藏!
- 华为将正式发布鸿蒙手机操作系统;清华成立量子信息班;美团:外卖是微利业务,直接降低抽成无法持续|极客头条...
- 017-Centos7.6+CDH 6.2 安装和使用
- Mybatis 常见知识点问题
- 频繁刷新页面websocket会报错_代码优化:Node+WebSocket+Vue聊天室
- 2019.03.30 图解HTTP
- linux 制作分区镜像img文件
- PS中的颜色深度1位8位16位32位的解释
- C++笔试题目大全(笔试宝典)
- Qt tableWidget导入\导出Excel表格
- 藏锐 计算机硕士,指导项目一_认识计算机系统课件
- 【Android开发】微信精选,文章资讯类App开发记录总结
- 用开源的协同办公OA项目,做一个考勤系统
- rust怎么拆除墙壁指令_腐蚀RUST指令大全
- 函数指针的强制类型转换与void指针
- 想要写好文案,就要学习这八种动物
- 大数据相关总结(待续)
热门文章
- 数控g71编程实例带图_数控车床g71怎么编程?请举个例子谢谢了
- Android技术分享| 实现视频连麦直播
- 复现awvs——POODLE 攻击(带 CBC 密码套件的 SSLv3—CVE-2014-3566)
- 点是否在三角形内——C++实现
- DTL相关知识整理初稿
- 【https】利用keytool进行证书配置
- LNMP详解(九)——Nginx虚拟IP实战
- org.hibernate.StaleStateException: Batch update returned unexpected row cou...
- 联发科mt6779(Helio P90),mt6775(Helio P70),MT6771(Helio P60),Helio P35,MT6762(Helio P22)处理器参数介绍
- php数据存储mysql_php mysqli 存储数据库