声明:本文为博主原创文章,转载请注明出处:小嵩的博客

本系列传送门:
微信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 热修复介绍及接入(一)相关推荐

  1. 微信tinker 热修复

    Tinker 是微信官方的Android热补丁解决方案,它支持动态下发代码.So库以及资源,让应用能够在不需要重新安装的情况下实现更新.当然,你也可以使用Tinker来更新你的插件. github:h ...

  2. Tinker热修复初探

    听说热修复已经很久了,但这是第一次尝试去应用它.所以我对其它各种热修复也没什么了解,这里仅仅记下如何使用Tinker热修复. 对于Tinker热修复的介绍和问题这里也不写了,因为官方文档已经有了,戳这 ...

  3. 【错误记录】集成 Tinker 热修复报错 ( No such property: variantConfiguration for class: .ApplicationVariantData )

    文章目录 一.报错信息 二.解决方案 一.报错信息 接入 Tinker 热修复 , 使用如下 Gradle 插件 , // Tinker 的 tinker-patch-gradle-plugin 插件 ...

  4. android Tinker 热修复 乐固加固后友盟打多渠道包之后的补丁失效

    继上一篇 android tinker 热修复使用及注意事项  生成了热修复的补丁; 现在的需求是这样的,我想把这个包用腾讯乐固加固,然后生成多渠道包,希望这个补丁能修复所有这些渠道的包,经过测试,直 ...

  5. 腾讯Tinker 热修复 Andriod studio 3.0 配置和集成(二)多渠道打包和补丁发布

    腾讯Tinker 热修复 Andriod studio 3.0 多渠道打包和发布补丁方式推荐 本文说明 在之前我已经分享了Tinker 热修复的 Andriod studio3.0 初次配置和集成,时 ...

  6. (十四)Tinker 热修复原理及手写实现

    版权声明:本文为博主原创文章,未经博主允许不得转载. 本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下. 一.热修复 热补丁方案有很多,其中比较出名的有阿里的 AndFix.美团的 ...

  7. 【错误记录】Tinker 热修复示例运行报错 ( patch receive fail: /storage/emulated/0/patch_signed_7zip.apk, code: -2)

    文章目录 一.报错信息 二.解决方案 参考 [Android 热修复]运行 Tinker 官方示例 博客 ; 一.报错信息 Tinker 热修复中 , 将生成的 patch 包 app-debug-p ...

  8. tinker热修复gradle接入

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 2021-04-29更新,最近在复习热修复,发现我的老代码不行了,所以把这篇文章更新一下 本篇是gradle接入 tinker的gi ...

  9. 腾讯Tinker 热修复 Andriod studio 3.0 配置和集成(一)

    本文说明 面试的时候经常问我有没有用过热修复?用谁的?能说下原理吗?当时我回答得不好,毕竟以前的项目都没有用,又不敢装逼,mmp,但是基本流程还是知道的,所以我们来初探下Tinker 这个热修复,如果 ...

  10. Tinker 热修复框架模拟使用

    导言 前不久,腾讯推出了"微信小程序"这一概念,对移动原生应用的影响可谓巨大.而几乎就在同时, 腾讯在GitHub上开源了第一个项目Tinker, 这是一个Android平台的应用 ...

最新文章

  1. java hello work_Java入门教程系列 – 第一个程序 “hello, world”
  2. HH SaaS电商系统的供应商系统设计
  3. 使用js简单实现javaMap
  4. 在网页中放入贴纸插画是怎样的体验?这样的UI素材,你还不收藏!
  5. 华为将正式发布鸿蒙手机操作系统;清华成立量子信息班;美团:外卖是微利业务,直接降低抽成无法持续|极客头条...
  6. 017-Centos7.6+CDH 6.2 安装和使用
  7. Mybatis 常见知识点问题
  8. 频繁刷新页面websocket会报错_代码优化:Node+WebSocket+Vue聊天室
  9. 2019.03.30 图解HTTP
  10. linux 制作分区镜像img文件
  11. PS中的颜色深度1位8位16位32位的解释
  12. C++笔试题目大全(笔试宝典)
  13. Qt tableWidget导入\导出Excel表格
  14. 藏锐 计算机硕士,指导项目一_认识计算机系统课件
  15. 【Android开发】微信精选,文章资讯类App开发记录总结
  16. 用开源的协同办公OA项目,做一个考勤系统
  17. rust怎么拆除墙壁指令_腐蚀RUST指令大全
  18. 函数指针的强制类型转换与void指针
  19. 想要写好文案,就要学习这八种动物
  20. 大数据相关总结(待续)

热门文章

  1. 数控g71编程实例带图_数控车床g71怎么编程?请举个例子谢谢了
  2. Android技术分享| 实现视频连麦直播
  3. 复现awvs——POODLE 攻击(带 CBC 密码套件的 SSLv3—CVE-2014-3566)
  4. 点是否在三角形内——C++实现
  5. DTL相关知识整理初稿
  6. 【https】利用keytool进行证书配置
  7. LNMP详解(九)——Nginx虚拟IP实战
  8. org.hibernate.StaleStateException: Batch update returned unexpected row cou...
  9. 联发科mt6779(Helio P90),mt6775(Helio P70),MT6771(Helio P60),Helio P35,MT6762(Helio P22)处理器参数介绍
  10. php数据存储mysql_php mysqli 存储数据库