Android热修复之微信Tinker使用初探

文章地址:Android热修复之微信Tinker使用初探

前几天,万众期待的微信团队的Android热修复框架tinker终于在GitHub上开源了。

地址:https://github.com/Tencent/tinker

官方介绍:https://my.oschina.net/shwenzhang/blog/751618

接入指南:https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97

今天拿下来集成使用了一下,发现md上对集成使用的过程介绍的比较精简(后来发现wiki上面倒是很详细,需要的同学可以自己去看),这里记录一下我集成使用的过程。

一、集成

我这里直接使用的是他1.6.2版本(1.6.0-1.6.3集成与使用的应该都类似,但是1.7.0以及以后,修改比较大,配置与使用略有不同)自带的sample,导入到studio中。

二、初始化配置

首先我们需要在app/bulid.gradle中,设置tinkerId的值,很多人开始编译就报错,提示“tinkerId is not set!!!”,就是因为这个值没有设置。获取tinkerId走的

[java] view plain copy

  1. def gitSha() {
  2. return ‘git rev-parse --short HEAD‘.execute().text.trim()
  3. }

这个方法,也就是获取git的版本号,所以要是你没有安装配置git,或者git没有加入到环境变量中,会得不到git的版本号了。知道了原理,那解决方式就自己想了,我这里就直接写死,上面这个方法直接返回固定字符串。

---------------------------------------------------------------------------

补充:关于获取Git提交版本号

[java] view plain copy

  1. git rev-parse --short HEAD

这段代码主要是用来显示最近一次提交到HEAD上的记录编号(类似于“b03b0c4”的字符串。个人对git命令行了解不多,如果有知道的大神麻烦指教一下)。

所以前面说的,除了环境变量配置好git(可以在命令行输入 git --version ,显示出了版本号,便是配置成功),再把你的项目与git关联起来,并且保证有一次提交记录,才能获取到该字符串。

具体使用可以看我的另一篇文章:关于git命令“git rev-parse --short HEAD”在android studio中使用与配置的个人探究

我在使用sample时先直接写死了。正式应用到项目上时,可以根据自己的实际情况来配置该参数。

---------------------------------------------------------------------------------------------------

之后,我们会看到Manifest.xml中,SampleApplication.Java这个类报红找不到。这个并不影响,因为到时候我们在编译的时候,tinker会为我们生成SampleApplication.java这个类,而起作用的代码就是SampleApplicationLike.java类声明上面的

[java] view plain copy

  1. @DefaultLifeCycle(application = "tinker.sample.android.app.SampleApplication",
  2. flags = ShareConstants.TINKER_ENABLE_ALL,
  3. loadVerifyFlag = false)

这段注解,我们也可以把这段注解注释了,

自定定义一个SampleApplication类继承 TinkerApplication类,然后加入无参构造方法,得到的类为:

[java] view plain copy

  1. /**
  2. * Created by anzyhui on 2016/9/26.
  3. */
  4. public class SampleApplication extends TinkerApplication {
  5. public SampleApplication(){
  6. super(
  7. //tinkerFlags, which types is supported
  8. //dex only, library only, all support
  9. ShareConstants.TINKER_ENABLE_ALL,
  10. // This is passed as a string so the shell application does not
  11. // have a binary dependency on your ApplicationLifeCycle class.
  12. "tinker.sample.android.app.SampleApplicationLike");
  13. }
  14. }

这些都是Tinker这边定好的规矩,咱们得照着来(第二个参数中的".app"不要忘了,也就是路径不要搞错了),其中第一个参数表示支持修复的类型,有以下类型可以选:

[java] view plain copy

  1. public static final int TINKER_DISABLE             = 0x00;
  2. public static final int TINKER_DEX_MASK            = 0x01;
  3. public static final int TINKER_NATIVE_LIBRARY_MASK = 0x02;
  4. public static final int TINKER_RESOURCE_MASK       = 0x04;
  5. public static final int TINKER_DEX_AND_LIBRARY     = TINKER_DEX_MASK | TINKER_NATIVE_LIBRARY_MASK;
  6. public static final int TINKER_ENABLE_ALL          = TINKER_DEX_MASK | TINKER_NATIVE_LIBRARY_MASK | TINKER_RESOURCE_MASK;

从字面理解就知道什么意思了。

重新编译一下,一般也不会有问题,如果报类重复的异常的话,看看你是不是之前已经生成过了SampleApplication了,在\build\intermediates\classes\debug\tinker路径下对应的目录里,有的话,删掉再编译试下。

三、功能测试

(1)debug版本

先尝试debug版本

现在我们模拟打包一个出现bug的版本。

再activity_main.xml中加入俩控件:

[html] view plain copy

  1. <TextView
  2. android:id="@+id/tvMessage"
  3. android:layout_width="wrap_content"
  4. android:layout_height="wrap_content"
  5. android:layout_below="@+id/showInfo"
  6. android:text="一切正常"    />
  7. <Button
  8. android:id="@+id/btnBug"
  9. android:layout_width="wrap_content"
  10. android:layout_height="wrap_content"
  11. android:layout_alignParentLeft="true"
  12. android:layout_alignParentStart="true"
  13. android:layout_below="@+id/tvMessage"
  14. android:text="点击出现bug"/>

MainActivity中设置点击事件,点击btnBug时,tvMessage显示"出现bug了"文字。



打包生成apk
这时,在build/bakApk目录中,会生成一个apk和一个R.txt文件,R.txt的作用后面再讲。

apk运行至手机中,
点击点击btnBug按钮,显示:


好,现在来修复这个bug。

我们在activity_main.xml中删除该“点击出现bug”按钮,增加“点击修复bug”按钮:
(PS:也就是修改了res文件)
当然了MainActivity中的代码中,也修改了点击事件,删除了“点击出现bug”按钮的点击事件,增加了“点击修复bug”按钮的点击事件。
点击按钮后,上方文本要显示“bug已经修复了”。

代码改好后,我们需要配置一下前面提到了R.txt文件,这里面保存了每次编译得到的每个res文件的id值。

具体配置就是,在app/build.gradle的ext部分,添加oldApk(也就是出现bug的那个apk)的信息:

[java] view plain copy

  1. ext {
  2. //for some reason, you may want to ignore tinkerBuild, such as instant run debug build?
  3. tinkerEnabled = true
  4. //you should bak the following files
  5. //old apk file to build patch apk
  6. tinkerOldApkPath = "${bakPath}/app-debug-0927-11-47-21.apk"  //这个是你出现bug的apk完整名称,app/build/bakApk目录下
  7. //proguard mapping file to build patch apk
  8. tinkerApplyMappingPath = "${bakPath}/"  //暂未开启混淆,不用管
  9. //resource R.txt to build patch apk, must input if there is resource changed
  10. tinkerApplyResourcePath = "${bakPath}/app-debug-0927-11-47-21-R.txt"  //如果你修复了res文件,需要指定你bug版本的R.txt文件
  11. }
然后,在studio的右上角,打开gradle,找到tinkerPatchDebug(我一直用的是debug签名,这个根据自己实际情况来)这个task,点击运行。就会在output目录下生成一个tinkerpatch目录,
在里面找到patch_signed_7zip.apk和patch_signed.apk,这就是我们的差分包。

因为代码中指定的差分包路径为:

[java] view plain copy

  1. loadPatchButton.setOnClickListener(new View.OnClickListener() {
  2. @Override
  3. public void onClick(View v) {
  4. TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk");
  5. }
  6. });
我们就直接使用patch_signed_7zip.apk,放到我们的手机根目录,再打开app,点击LOAD PATCH,


提示我们补丁加载成功,重启后生效。

这里,我们需要杀掉本进程,再进入app,才能应用补丁包修复成功。

有朋友评论说自己也加载成功但没法修复,是不是按得返回键退出(返回键退出时是activity销毁,进程还在)。
杀进程后再进入 ,应该就可以修复成功了,如果不成功,把补丁包逆向一下,看看自己修复的部分有没有在里面。




重启后,注意看界面上的按钮是不是已经被替换,

按钮已经替换了,这里我已经点击了按钮,所以上方文本内容也提示修复成功。

(2)release版本+混淆

release版本总体使用方式与的bug版本类似,

每次打出正式包时,我们应该备份好。

在打包前,修改app/build.gradle里的

[plain] view plain copy

  1. /**
  2. * task type, you want to bak
  3. */
  4. //def taskName = "debug"
  5. def taskName = "release"

tastName为release,minifyEnabled设置为true,

这样release打包时才会在bakApk下生成对应的apk,R.txt以及mapping文件。

之后的打包是一样的。

打差分包(补丁)的时候,我们要做的,同样是,替换里面的属性:

[plain] view plain copy

  1. ext {
  2. //for some reason, you may want to ignore tinkerBuild, such as instant run debug build?
  3. tinkerEnabled = true
  4. //you should bak the following files
  5. //old apk file to build patch apk
  6. tinkerOldApkPath = "${bakPath}/app-release-0929-11-28-36.apk"
  7. //proguard mapping file to build patch apk
  8. tinkerApplyMappingPath = "${bakPath}/app-release-0929-11-28-36-mapping.txt"
  9. //resource R.txt to build patch apk, must input if there is resource changed
  10. tinkerApplyResourcePath = "${bakPath}/app-release-0929-11-28-36-R.txt"
  11. }

开启了混淆的话,就要设置bug版本release包的mapping文件,以保证两次映射一致。

同样,右侧gradle,点击tinkerPatchRelease。

我这次Gradle任务进行了好几分钟,当时还以为是出错了,后来看了一下下图,

因为我是第一次打release包,所以在下载必要的jar包。

有运行慢的可以点进这里看看,是不是一样的情况。

差分包打包成功后,跟之前一样的操作,我这边测试成功没问题呢。

四、总结

之前研究过AndFix,主要是使用native方法,来修改出现bug的方法,虽然支持的系统版本也很广(2.3~7.0),但是具有一定的局限性,只能修改方法的内部实现,不支持新增方法,新增、修改成员变量等;而基于ClassLoader的各种实现,由于采用在字节码中在构造方法注入一段代码,防止被打上CLASS_ISPREVERIFIED标记,在dalvik中比较影响性能,而且支持系统也不全面,所以都不是特别的完美。而且他们都有一个缺点,不能修改资源文件。

这次Tinker的发布,打破了原有的性能问题,功能局限,实现了,支持对library、java类、res文件的修复,并且除了小部分版本的设备(wiki上说是部分三星api19版本),其他基本都可以覆盖到(yunOS另说)。相信在wx几位大神的维护下,tinker会越来越好。官方Tinker热补丁技术交流群:377388954

时间: 2024-08-25 15:58:06

Android热修复之微信Tinker使用初探的相关文章

Android热修复——Tinker微信解决方案

Android的热修复 前言: 随着时代的发展,由于公司的项目需要去求变化平凡计划总赶不上变化,H5的高灵活性,开发周期短,更新速度快H5以及一些混合开发越来越被看好,然而主要原因之一:这种混合开发的方式容错率大,更新和修复BUG快.不用发布版本就可以让用户不觉的情况下就更新对应的内容或者BUG,我们不能否认混合开发的快捷,正在此前提下热修复和热更新技术也得到了非常大的发展,不管热修复还是热更新,都是对app的内容或者逻辑变化做出像web页面更新一样的体验.而本文只对热修复进行探索,不对H5进行

Android热修复技术专题:来自微信、淘宝、支付宝、QQ空间的热修复方案

最近好多人都讨论关于热更新的话题,所以查询了一些资料看看 当一个App发布之后,突然发现了一个严重bug需要进行紧急修复,这时候公司各方就会忙得焦头烂额:重新打包App.测试.向各个应用市场和渠道换包.提示用户升级.用户下载.覆盖安装.有时候仅仅是为了修改了一行代码,也要付出巨大的成本进行换包和重新发布. 这时候就提出一个问题:有没有办法以补丁的方式动态修复紧急Bug,不再需要重新发布App,不再需要用户重新下载,覆盖安装?答案当然是有的,那就是最近涌现出来得热补丁方案,主要包括淘宝的Dexpo

Android 热修复 Tinker 源码分析之DexDiff / DexPatch

在上一篇文章中,我们介绍了Android 热修复 Tinker接入及源码浅析,里面包含了热修的一些背景知识,从tinker对dex文件的处理来看,源码大体上可以分为3部分阅读: 在应用中对patch的合并与加载,已经在上篇文章中详细介绍过了Android 热修复 Tinker接入及源码浅析 详细的dex patch,dex diff算法 tinker gradle plugin相关知识 tinker有个非常大的亮点就是自研发了一套dex diff.patch相关算法.本篇文章主要目的就是分析该算

Android 热修复方案分析

绝大部分的APP项目其实都需要一个动态化方案,来应对线上紧急bug修复发新版本的高成本.之前有利用加壳,分拆两个dex结合DexClassLoader实现了一套全量更新的热更方案.实现原理在Android 基于Proxy/Delegate 实现bug热修复这篇博客中有分解.因为这套方案是在Java端实现,并且是全量更新所以兼容性较好,成功率较高.但是在线上跑了几个月之后就碰到了瓶颈,因为随着业务的增长分拆过之后的dex文件方法数也超过65535个,更换拆包方案的话维护成本太高.同时由于没有做差异

全面了解Android热修复技术

WeTest 导读 本文探讨了Android热修复技术的发展脉络,现状及其未来. 热修复技术概述 热修复技术在近年来飞速发展,尤其是在InstantRun方案推出之后,各种热修复技术竞相涌现.国内大部分成熟的主流APP都拥有自己的热修复技术,像手淘.支付宝.QQ.饿了么.美团等等. 目前能搜集到的资料,大多简单罗列每个方案的特点并进行横向比较,而其中技术发展的脉络往往被掩盖了.热修复技术从何而来,又将往何处去?在这些资料中都找不到答案. 我认为,走马观花地看一遍各家的热修复方案并不能找到答案,所

Android热修复框架汇总整理(Hotfix)

??Android平台出现了一些优秀的热更新方案,主要可以分为两类:一类是基于multidex的热更新框架,包括Nuwa.Tinker等:另一类就是native hook方案,如阿里开源的Andfix和Dexposed. 基于native hook的方案 ??需要针对dalvik虚拟机和art虚拟机做适配,需要考虑指令集的兼容问题,需要native代码支持,兼容性上会有一定的影响: 基于Multidex的方案 ??需要反射更改DexElements,改变Dex的加载顺序,这使得patch需要在下

Android热修复与插件化实践之路

第1章 class文件与dex文件解析本章通过从java最基本的class文件与android最基本的dex文件进行对比,并不借助IDE去生成及执行class与dex文件,通过讲解class与dex的手动生成,执行, 格式对比,让学生明白二者的相同与不同.1-1 课程项目整体介绍1-2 本章概述1-3 class文件详解上1-4 class文件详解下1-5 dex文件详解上1-6 dex文件详解下 第2章 虚拟机深入讲解本章主要介绍jvm,dvm,art.通过对这三个虚拟机的介绍让学生明白,an

聊聊Android 热修复Nuwa有哪些坑

原创地址:http://blog.csdn.net/sbsujjbcy/article/details/51028027 前面写了两篇关于Nuwa的文章 Android 热修复Nuwa的原理及Gradle插件源码解析 Android 热修复使用Gradle Plugin1.5改造Nuwa插件 然后我说了Nuwa有坑,有人就问Nuwa到底有哪些坑,这篇文章对自己在Nuwa上走过的坑做一个总结,如果你遇到了其他坑,欢迎留言,我会统一加到文章中去.当然有些也不算是Nuwa的坑,算是ClassLoade

Android热修复:Andfix和Hotfix,两种方案的比较与实现

Andfix和hotfix是两种android热修复框架. android的热修复技术我看的最早的应该是QQ空间团队的解决方案,后来真正需要了,才仔细调查,现在的方案中,阿里有两种Dexposed和Andfix框架,由于前一种不支持5.0以上android系统,所以阿里系的方案我们就看Andfix就好.Hotfix框架算是对上文提到的QQ空间团队理论实现.本文旨在写实现方案,捎带原理. Andfix 引入 框架官网:https://github.com/alibaba/AndFix 介绍是用英文