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

Android的热修复

前言:

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

不久前微信开源了Tinker,github的star数量直飚5000+,我的天,还在等什么,学习学习.

什么是热修复



热修复补丁(hotfix),又称为patch,指能够修复软件漏洞的一些代码,是一种快速、低成本修复产品软件版本缺陷的方式。

前言中描述的”不用发布版本就可以让用户不觉的情况下就更新对应的内容或者BUG”可能不算准确,所以我自行百度了一下.

热修复说白了就是”打补丁”,比如你们公司上线一个app,用户反应有重大bug,需要紧急修复。如果按照通

常做法,那就是程序猿加班搞定bug,然后测试,重新打包并发布。这样带来的问题就是成本高,效率低。于是,热

修复就应运而生.一般通过事先设定的接口从网上下载无Bug的代码来替换有Bug的代码。这样就省事多了,用

户体验也好.

原理

类似与插件开发,关于插件开发原理,看这篇 Android插件原理剖析 ,其中介绍了一下Java中的类加载器和Android中的类加载器.
热修复就是利用android中的 DexClassLoader 类加载器,动态加载补丁dex,替换有bug的类

已有的热修复解决方案:

微信Tinker

Tinker的github地址:https://github.com/Tencent/tinke

Tinker原理:微信Android热补丁实践演进之路

官方给出的定义:

Tinker is a hot-fix solution library for Android, it supports dex, library and resources update without reinstalling
apk.

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

这里原理以及好处.在这里就BB了,我们开发人员只需要关心怎么使用,实现就可以了.不过这里还是贴出来给大家学习..那么接下来直接实践作为一个资深的开发人员学习一个新的技术,第一想到就是去官网看看文档跑跑Demo,当然我也不例外(资深).

导入Sample工程

  • tinkerd地址,下载下来解压打开导入Android Studio,我们只需要把tinker-sample-android这个目录导入即可.
  • 导入之后,构建一下,想都不用想肯定出错,提示“tinkerId is not set!!!”,WTF????然后我们肯定会去看他的接入指南,前面一大堆BBBB… 

    看到了Sample的使用方法内心激动起来以为可以知道了什么原因了,再次WTF???没有直接就是运行的后的说明,不能忍,于是我又去网上找找,算是找到了解决的办法,但是后面才知道这些问题微信维护开源人员被问了烦了,直接列出了常见问题,我都不知道说什么了…….

    问题解决:这是因为没有正确的配置IDE的git路径, 若不是通过clone方式下载tinker,需要本地手动commit一次。这里你也可以使用其他字符作为tinkerId;

我这里的话直接就把当前的版本号作为id..

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

1git rev-parse –short HEAD

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

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

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

个人觉得,加入这段代码,显得更麻烦了,还不如直接写死,或者获取其他的版本号。

编译运行原版apk

  • 接下使用assembleDebug命令,再拿到下图中的app-debug-xxxxx.apk装在手机上运行

或者直接运行(不过要先关闭Instant Run) ->file->setting->Build.E….->Instant Run 第一个去掉就可以运行了

配置原版apk路径

这里的oldapkpath是上图编译运行原版apk中得到的apk路径和R.txt路径配置下就ok

if (buildWithTinker()) {
apply plugin: ‘com.tencent.tinker.patch‘

tinkerPatch {
     * the old apk path, use to diff with the new apk to build
     * add apk from the build/bakApk
     */
    oldApk = "${bakPath}/app-debug-1108-13-43-27.apk"
    ignoreWarning = false
    useSign = true
    buildConfig {

        applyMapping = getApplyMappingPath()

        applyResourceMapping = getApplyResourceMappingPath()

        tinkerId = getTinkerIdValue()
    }

这里的oldapk也要修改成上面编译运行原版apk生成apk的路径

修改源码 生成新版apk 补丁

  • 运行起来之后,打开代码MianActvity,修改代码,打开Log.e(TAG, “i am on onCreate string:” + getResources().getString(R.string.test_resource))的注释
  • 再运行下面图中的tinkerPatchDebug,或者在Terminal使用gradlew tinkerPatchDebug ,Terminal->就是 

    Android studio 一般左下角的那个cmd控制台一样的东西

这样在app/build/outputs/tinkerPatch/debug/patch_signed_7zip.apk路径下找到这个差异包,也就是我们俗称的补丁.

推送补丁

然后把patch_signed_7zip.apk放到手机SD卡中去使用命令

adb push ./app/build/outputs/tinkerPatch/debug/patch_signed_7zip.apk /storage/sdcard0/

这里放置的路径与apk中获取补丁位置一致

运行应用,加载补丁

再次运行apk,点击LoadPatch时调用TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), Environment.getExternalStorageDirectory().getAbsolutePath()
+ "/patch_signed_7zip.apk");
方法,加载补丁.

查看控制台日志,打印出i am on onCreate string:I am in the base apk 就表示成功了



补充:返回键退出后进入,并没有执行修复。

(当时以为是我手机的原因,就没太在意),现在有朋友评论说自己也加载成功但没法修复,是不是跟我一样按得返回键退出。

杀进程后再进入 ,应该就可以修复成功了,如果不成功,把补丁包逆向一下,看看自己修复的部分有没有在里面。


集成到自己的项目中

1. 添加gradle依赖

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

buildscript {
    dependencies {
        classpath (‘com.tencent.tinker:tinker-patch-gradle-plugin:1.7.3‘)
    }
}
然后在app的gradle文件app/build.gradle,我们需要添加tinker的库依赖以及apply tinker的gradle插件.

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

2. 添加生成补丁方法

tinkerPatch {
//有问题的apk的地址,就是要修复BUG的那个apk,这是在电脑上位置
oldApk = "D://1//app-debug-old.apk"
ignoreWarning = false
useSign = true
buildConfig {
    tinkerId = "1.0"
}
packageConfig {
    //写这个为了修复一个bug,详见github issue #22
    configField("TINKER_ID", "1.0")
}
dex {
    dexMode = "jar"
    pattern = ["classes*.dex", "assets/secondary-dex-?.jar"]
    loader = ["com.tencent.tinker.loader.*", "com.kairu.rxjava.app.MyApplicationLike"]
}
lib {
    pattern = ["lib/armeabi/*.so", "lib/arm64-v8a/*.so", "lib/armeabi-v7a/*.so", "lib/mips/*.so", "lib/mips64/*.so", "lib/x86/*.so", "lib/x86_64/*.so"]
}
res {
    pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
    largeModSize = 100
}
sevenZip {
    zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
}

}

3. 配置Application

程序启动时会加载默认的Application类,这导致我们补丁包是无法对它做修改了。如何规避?在这里我们并没有使用类似InstantRun hook Application的方式,而是通过代码框架的方式来避免,这也是为了尽量少的去反射,提升框架的兼容性。

这里我们要实现的是完全将原来的Application类隔离起来,即其他任何类都不能再引用我们自己的Application。我们需要做的其实是以下几个工作:

将我们自己Application类以及它的继承类的所有代码拷贝到自己的ApplicationLike继承类中,例如SampleApplicationLike。你也可以直接将自己的Application改为继承ApplicationLike;

Application的attachBaseContext方法实现要单独移动到onBaseContextAttached中;

对ApplicationLike中,引用application的地方改成getApplication();

对其他引用Application或者它的静态对象与方法的地方,改成引用ApplicationLike的静态对象与方法;

更详细的事例,大家可以参考下面的一些例子以及SampleApplicationLike的做法。

这是我的例子:也可以参考https://github.com/Tencent/tinker/wiki/Tinker-%E8%87%AA%E5%AE%9A%E4%B9%89%E6%89%A9%E5%B1%95

@DefaultLifeCycle(
        application = "com.kairu.rxjava.app.MyApplication",//这的Application是以前项目中的MyApplication
        flags = ShareConstants.TINKER_ENABLE_ALL
)
public class MyApplicationLike extends DefaultApplicationLike {

    private static Application mApplication;
    public static String currentGirl = "http://ww2.sinaimg.cn/large/610dc034jw1f5k1k4azguj20u00u0421.jpg";

    public MyApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent, Resources[] resources, ClassLoader[] classLoader, AssetManager[] assetManager) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent, resources, classLoader, assetManager);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //这里把所有的Application换成getApplication() 原因看https://github.com/Tencent/tinker/wiki/Tinker-%E8%87%AA%E5%AE%9A%E4%B9%89%E6%89%A9%E5%B1%95
        mApplication = getApplication();

        //配置是否显示log
        LogUtil.isDebug = true;

        //配置时候显示toast
        ToastUtils.isShow = true;

        //配置程序异常退出处理
    }
    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);
        TinkerInstaller.install(this);
        //在初始化的时候调用加载补丁的方法,路径是实际补丁放的位置
        TinkerInstaller.onReceiveUpgradePatch(this.getApplication(), Environment.getExternalStorageDirectory().getAbsolutePath()+"/patch_signed_7zip.apk");

    }
    public static Context getContext() {
        return mApplication;
    }

    public static Application getIntstance() {
        return mApplication;
    }
}


是不是简单暴力就完了?当然配置就搞定了,没有那么复杂…..

最后我们都配置好了那怎么得到补丁包呢?

也是一样

步骤1:编译运行原版apk

把生成的apk放在自己定义的路径下

tinkerPatch {
    ...
//有问题的apk的地址,就是要修复BUG的那个apk,这是在电脑上位置
oldApk = "D://1//app-debug-old.apk"
    ...
    }

步骤2:修改源码 生成新版apk 补丁

这里修改源码指的是实际项目中修复BUG更改的代码…

后续的步骤都一样就搞定了…………

Tinker的局限

如果出现以下的情况,并且ignoreWarning为false,我们将中断编译。因为这些情况可能会导致编译出来的patch包带来风险:

1. minSdkVersion小于14,但是dexMode的值为”raw”;

2. 新编译的安装包出现新增的四大组件(Activity, BroadcastReceiver…);

3. 定义在dex.loader用于加载补丁的类不在main dex中;

4. 定义在dex.loader用于加载补丁的类出现修改;

5. resources.arsc改变,但没有使用applyResourceMapping编译。

还有就是需要结束当前进程才能进行修复....

[指南详情](https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97)

博客慢慢完善….

参考文献:

http://www.tuicool.com/articles/2i67reV Android 热修复总结

https://github.com/Tencent/tinker tinker项目

https://github.com/Tencent/tinker/wiki tinker wiki

https://github.com/Tencent/tinker/wiki/Tinker-%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98 常见问题

时间: 2024-08-02 10:58:27

Android热修复——Tinker微信解决方案的相关文章

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%9

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

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

Android 热修复 Tinker接入及源码浅析

本文已在我的公众号hongyangAndroid首发.转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/54882693本文出自张鸿洋的博客 一.概述 放了一个大长假,happy,先祝大家2017年笑口常开. 假期中一行代码没写,但是想着马上要上班了,赶紧写篇博客回顾下技能,于是便有了本文. 热修复这项技术,基本上已经成为项目比较重要的模块了.主要因为项目在上线之后,都难免会有各种问题,而依靠发版去修复问题,成本太高了. 现在热

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

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

Android 热修复方案分析

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

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

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

全面了解Android热修复技术

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

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

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

Android热修复学习之旅——HotFix完全解析

在上一篇博客Android热修复学习之旅开篇--热修复概述中,简单介绍了各个热修复框架的原理,本篇博客我将详细分析QQ空间热修复方案. Android dex分包原理介绍 QQ空间热修复方案基于Android dex分包基础之上,简单概述android dex分包的原理就是:就是把多个dex文件塞入到app的classloader之中,但是android dex拆包方案中的类是没有重复的,如果classes.dex和classes1.dex中有重复的类,当classes.dex和classes1