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走的
- def gitSha() {
- return 'git rev-parse --short HEAD'.execute().text.trim()
- }
这个方法,也就是获取git的版本号,所以要是你没有安装配置git,或者git没有加入到环境变量中,会得不到git的版本号了。知道了原理,那解决方式就自己想了,我这里就直接写死,上面这个方法直接返回固定字符串。
---------------------------------------------------------------------------
补充:关于获取Git提交版本号
- 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类声明上面的
- @DefaultLifeCycle(application = "tinker.sample.android.app.SampleApplication",
- flags = ShareConstants.TINKER_ENABLE_ALL,
- loadVerifyFlag = false)
这段注解,我们也可以把这段注解注释了,
自定定义一个SampleApplication类继承 TinkerApplication类,然后加入无参构造方法,得到的类为:
- /**
- * Created by anzyhui on 2016/9/26.
- */
- public class SampleApplication extends TinkerApplication {
- public SampleApplication(){
- super(
- //tinkerFlags, which types is supported
- //dex only, library only, all support
- ShareConstants.TINKER_ENABLE_ALL,
- // This is passed as a string so the shell application does not
- // have a binary dependency on your ApplicationLifeCycle class.
- "tinker.sample.android.app.SampleApplicationLike");
- }
- }
这些都是Tinker这边定好的规矩,咱们得照着来(第二个参数中的".app"不要忘了,也就是路径不要搞错了),其中第一个参数表示支持修复的类型,有以下类型可以选:
- public static final int TINKER_DISABLE = 0x00;
- public static final int TINKER_DEX_MASK = 0x01;
- public static final int TINKER_NATIVE_LIBRARY_MASK = 0x02;
- public static final int TINKER_RESOURCE_MASK = 0x04;
- public static final int TINKER_DEX_AND_LIBRARY = TINKER_DEX_MASK | TINKER_NATIVE_LIBRARY_MASK;
- 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中加入俩控件:
- <TextView
- android:id="@+id/tvMessage"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/showInfo"
- android:text="一切正常" />
- <Button
- android:id="@+id/btnBug"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_below="@+id/tvMessage"
- 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)的信息:
- ext {
- //for some reason, you may want to ignore tinkerBuild, such as instant run debug build?
- tinkerEnabled = true
- //you should bak the following files
- //old apk file to build patch apk
- tinkerOldApkPath = "${bakPath}/app-debug-0927-11-47-21.apk" //这个是你出现bug的apk完整名称,app/build/bakApk目录下
- //proguard mapping file to build patch apk
- tinkerApplyMappingPath = "${bakPath}/" //暂未开启混淆,不用管
- //resource R.txt to build patch apk, must input if there is resource changed
- tinkerApplyResourcePath = "${bakPath}/app-debug-0927-11-47-21-R.txt" //如果你修复了res文件,需要指定你bug版本的R.txt文件
- }
然后,在studio的右上角,打开gradle,找到tinkerPatchDebug(我一直用的是debug签名,这个根据自己实际情况来)这个task,点击运行。就会在output目录下生成一个tinkerpatch目录,
在里面找到patch_signed_7zip.apk和patch_signed.apk,这就是我们的差分包。
因为代码中指定的差分包路径为:
- loadPatchButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk");
- }
- });
我们就直接使用patch_signed_7zip.apk,放到我们的手机根目录,再打开app,点击LOAD PATCH,
提示我们补丁加载成功,重启后生效。
这里,我们需要杀掉本进程,再进入app,才能应用补丁包修复成功。
有朋友评论说自己也加载成功但没法修复,是不是按得返回键退出(返回键退出时是activity销毁,进程还在)。
杀进程后再进入 ,应该就可以修复成功了,如果不成功,把补丁包逆向一下,看看自己修复的部分有没有在里面。
重启后,注意看界面上的按钮是不是已经被替换,
按钮已经替换了,这里我已经点击了按钮,所以上方文本内容也提示修复成功。
(2)release版本+混淆
release版本总体使用方式与的bug版本类似,
每次打出正式包时,我们应该备份好。
在打包前,修改app/build.gradle里的
- /**
- * task type, you want to bak
- */
- //def taskName = "debug"
- def taskName = "release"
tastName为release,minifyEnabled设置为true,
这样release打包时才会在bakApk下生成对应的apk,R.txt以及mapping文件。
之后的打包是一样的。
打差分包(补丁)的时候,我们要做的,同样是,替换里面的属性:
- ext {
- //for some reason, you may want to ignore tinkerBuild, such as instant run debug build?
- tinkerEnabled = true
- //you should bak the following files
- //old apk file to build patch apk
- tinkerOldApkPath = "${bakPath}/app-release-0929-11-28-36.apk"
- //proguard mapping file to build patch apk
- tinkerApplyMappingPath = "${bakPath}/app-release-0929-11-28-36-mapping.txt"
- //resource R.txt to build patch apk, must input if there is resource changed
- tinkerApplyResourcePath = "${bakPath}/app-release-0929-11-28-36-R.txt"
- }
开启了混淆的话,就要设置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
Android热修复之微信Tinker使用初探的更多相关文章
- Android插件化与热修复(六)-微信Tinker原理分析
Tinker热修复原理分析 热补丁技术是在用户不需要重新安装应用的情况下实现应用更新,可快速解决一些线上问题.热补丁省去了Android应用发布版本的成本,而且用户端的更新也是无感知的. Tinker ...
- Android 热修复方案Tinker
转自:http://blog.csdn.net/l2show/article/details/53925543 Android 热修复方案Tinker(一) Application改造 Android ...
- Android 热修复方案Tinker(一) Application改造
基于Tinker V1.7.5 Android 热修复方案Tinker(一) Application改造 Android 热修复方案Tinker(二) 补丁加载流程 Android 热修复 ...
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
[原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...
- 全面了解Android热修复技术
WeTest 导读 本文探讨了Android热修复技术的发展脉络,现状及其未来. 热修复技术概述 热修复技术在近年来飞速发展,尤其是在InstantRun方案推出之后,各种热修复技术竞相涌现.国内大部 ...
- Android热修复框架汇总整理(Hotfix)
Android平台出现了一些优秀的热更新方案,主要可以分为两类:一类是基于multidex的热更新框架,包括Nuwa.Tinker等:另一类就是native hook方案,如阿里开源的Andfix ...
- Android热修复技术原理详解(最新最全版本)
本文框架 什么是热修复? 热修复框架分类 技术原理及特点 Tinker框架解析 各框架对比图 总结 通过阅读本文,你会对热修复技术有更深的认知,本文会列出各类框架的优缺点以及技术原理,文章末尾简单 ...
- Android热修复技术总结
https://blog.csdn.net/xiangzhihong8/article/details/77718004 插件化和热修复技术是Android开发中比较高级的知识点,是中级开发人员通向高 ...
- Android热修复技术原理详解
阿里Dexposed -- native解决方案 原理: 直接在native层进行方法的结构体信息对换,从而实现完美的方法新旧替换,从而实现热修复功能 他的思想完全来源于Xposed框架,完美诠释 ...
随机推荐
- javascript中通过匿名函数进行事件绑定
- matlab GUI界面编程总结
去年做了一些关于Matlab GUI的程序,现在又要做相关的东西,回想起来,当时很多经验没有记录下来,现在回顾起来始终觉得不爽,所以从现在开始,一定要勤写记录. 从简单的例子说起吧. 创建Matlab ...
- Ansible 学习笔记
最近因为需要管理很多台机器,而这些机器又需要频繁重新安装,实在受不了Puppet需要在每个客户机上都安装一遍,于是转头开始学些Ansible.根据这段时间的使用,这个确实是神器,唯一的感觉就是相见恨晚 ...
- SQL 递归
-- 查询指定部门下面的所有部门, 并汇总各部门的下级部门数 ) SET @Dept_name = N'MIS' ;WITH DEPTS AS( -- 查询指定部门及其下的所有子部门 -- 定位点成员 ...
- JDK1.6 中文API 下载地址
Java JDK 1.6 API 中文文档HTML版:点击下载 http://download.java.net/jdk/jdk-api-localizations/jdk-api-zh-cn/pub ...
- Python基础操作-函数
本节内容 1. 函数基本语法及特性2. 参数与局部变量3. 返回值4.递归5.高阶函数 1.函数基本语法及特性函数是什么? 函数一词来源于数学,但编程中的「函数」概念,与数学中的函数是有很大不同的,具 ...
- linux sed
sed 命令 sed -i 's/3306/3308/g' my.cnf mysql # 同时替换两个文件
- docker 与 vmware 的区别
- wcscpy_s与wcsncpy
今天运行以下代码时一直出错 void Foo(const wchar_t* lpch, int len) { ... wchar_t *str = ]; wcscpy_s(str, len, lpch ...
- eclipse 用links 安装插件
eclipse 用links 安装插件: 1.在eclipse目录下新建文件夹links 2.在links里新建文件 "控件名".link controlName.link 3.在 ...