Tinker热修复接入

在app上集成tinker,根据 tinker上的wiki 的指示操作即可。

具体步骤如下:

gradle接入

gradle是推荐的接入方式,在gradle插件tinker-patch-gradle-plugin中我们帮你完成proguard、multiDex以及Manifest处理等工作。

添加gradle依赖

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

buildscript {
    dependencies {
        classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.9.1')
    }
}

然后在app的gradle文件app/build.gradle,我们需要添加tinker的库依赖以及apply tinker的gradle插件.

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

tinkerPatch task详解

直接使用task:tinkerPatchVariantName(例如tinkerPatchDebug、tinkerPatchRelease)即可自动根据Variant选择相应的编译类型,同时它还贴心的为我们完成以下几个操作:

  1. 将TINKER_ID自动插入AndroidManifest的meta项,输出路径为build/intermediates/tinker_intermediates/AndroidManifest.xml;

  2. 如果minifyEnabled为true,将自动将Tinker的proguard规则添加到proguardFiles中,输出路径为build/intermediates/tinker_intermediates/tinker_proguard.pro,这里你不需要将它们拷贝到自己的proguard配置文件中;

  3. 如果multiDexEnabled为true,将自动生成Tinker需要放在主dex的keep规则。在tinker 1.7.6版本之前,你需要手动将生成规则拷贝到自己的multiDexKeepProguard文件中。例如Sample中的multiDexKeepProguard file("keep_in_main_dex.txt")。在1.7.6版本之后,这里会通过脚本自动处理,无须手动填写。

  4. 把dexOptions的jumboMode打开。

dexOptions {
     jumboMode = true
 }

输出路径为:build/intermediates/tinker_intermediates/tinker_multidexkeep.pro。 后你可以在build/outputs/tinkerPatch中找到输出的文件。

多Flavor打包

有的时候我们希望通过flavor方式打包,在sample中提供了简单的用法事例:

  1. 通过flavor编译,这个时候我们可以看到bakApk路径是一个按照flavor名称区分的目录;

  2. 将编译目录路径填写到sample中tinkerBuildFlavorDirectory,其他的几个字段不需要填写,这里会自动根据路径拼接;

ext {
    tinkerBuildFlavorDirectory = "${bakPath}/app-1014-13-35-12"
}
  1. 运行tinkerPatchAllFlavorDebug或者tinkerPatchAllFlavorRelease即可得到所有flavor的补丁包。

注:
对于渠道包,如果不是需要使用热修复,那么怎么生成渠道包都可以的。
对于flavor编译渠道包,会导致不同的渠道包由于BuildConfig变化导致classes.dex差异,这种方案是不可取的。
将渠道信息写在apk文件的zip comment中,是非常推荐的,例如可以使用项目 packer-ng-plugin 或者可使用V2 Scheme的 walle , 也包括最新出来的多渠道打包神器 ApkChannelPackage ,说一下区别,如果要使用热修复的话,对于不需要加固的app,那么生成渠道包,这三种方案都可以采用;对于要加固的app,只能采用ApkChannelPackage这种方案中的根据已有APK生成渠道包。 这篇文章 对多渠道打包工具对比做了详细的区分。目前采用的也是ApkChannelPackage方案。

Sample的使用方法

Demo请参考tinker-sample-android, 它的使用方法如下:

  1. 调用assembleDebug编译,我们会将编译过的包保存在build/bakApk中。然后我们将它安装到手机,点击SHOW INFO按钮,可以看到补丁并没有加载.

  2. 修改代码,例如将MainActivityI am on patch onCreate的Log打开。然后我们需要修改build.gradle中的参数,将步骤一编译保存的安装包路径拷贝到tinkerPatch中的oldApk参数中。

  3. 调用tinkerPatchDebug, 补丁包与相关日志会保存在/build/outputs/tinkerPatch/。然后我们将patch_signed_7zip.apk推送到手机的sdcard中。

    adb push ./app/build/outputs/tinkerPatch/debug/patch_signed_7zip.apk /storage/sdcard0/
    
  4. 点击LOAD PATCH按钮, 如果看到patch success, please restart process的toast,即可锁屏或者点击KILL SELF按钮

  5. 我们可以看到的确出现了I am on patch onCreate日志,同时点击SHOW INFO按钮,显示补丁包的确已经加载成功了。

Release的使用方法

Tinker的使用方式如下,以gradle接入的release包为例:

  1. 每次编译或发包将安装包与mapping文件备份;
  2. 若有补丁包的需要,按自身需要修改你的代码、库文件等;
  3. 将备份的基准安装包与mapping文件输入到tinkerPatch的配置中;
  4. 运行tinkerPatchRelease,即可自动编译最新的安装包,并与输入基准包作差异,得到最终的补丁包。

调试源码

tinker调试源码非常简单,大家需要在tinker的主工程运行tinker group中buildAndPublishTinkerToLocalMaven任务即可。

此外由于localmaven无法传递依赖,需要在使用的地方再显式引用以下库:

compile("com.tencent.tinker:tinker-android-loader:${TINKER_VERSION}") { changing = true }
compile("com.tencent.tinker:aosp-dexutils:${TINKER_VERSION}") { changing = true }
compile("com.tencent.tinker:bsdiff-util:${TINKER_VERSION}") { changing = true }
compile("com.tencent.tinker:tinker-commons:${TINKER_VERSION}") { changing = true }

github/Tinker的默认分支为master分支,几个含义的含义分别是:

  1. master分支;最近一次release的稳定代码,我们在master分支打tag;
  2. dev分支;开发分支,这里会包含下一个版本的代码,我们只能给dev分支提pr以及验证部分已经修复的issue;
  3. hotfix分支;为了修复tinker紧急bug的分支。

关于tinker分支管理、issue以及pr规范,请阅读Tinker Contributing Guide

为了将tinker单独分块更清晰,可以将tinker相关的代码整理到 tinkerpatch.gradle 中

加固支持

从1.7.8开始,tinker又支持加固了,只需要修改tinkerpatch.gradle中的这部分

buildConfig {
            applyMapping = getApplyMappingPath()
            applyResourceMapping = getApplyResourceMappingPath()
            tinkerId = getTinkerIdValue()
            keepDexApply = false

            isProtectedApp = true //开启加固
            ...
        }

补丁管理后台:

https://github.com/baidao/tinker-manager

一、集成Tinker app/build.gradle 配置,参考官方 sample,也可以参考SDK里的 sample

二、集成SDK

  1. app/build.gradle
repositories {
    jcenter()
}

dependencies {
    ...
    compile 'com.dx168.patchsdk:patchsdk:1.2.6'
}
  1. ApplicationLike
@SuppressWarnings("unused")
@DefaultLifeCycle(application = "com.dx168.patchsdk.sample.MyApplication",
        flags = ShareConstants.TINKER_ENABLE_ALL,
        loadVerifyFlag = false)
public class MyApplicationLike extends TinkerApplicationLike {

    private OriginalApplication originalApplication;

    public MyApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
        originalApplication = new OriginalApplication();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        String appId = "20170112162040035-6936";
        String appSecret = "d978d00c0c1344959afa9d0a39d7dab3";
        PatchManager.getInstance().init(getApplication(), "http://xxx.xxx.xxx/hotfix-apis/", appId, appSecret, new ActualPatchManager() {
            @Override
            public void cleanPatch(Context context) {
                TinkerInstaller.cleanPatch(context);
            }

            @Override
            public void patch(Context context, String patchPath) {
                TinkerInstaller.onReceiveUpgradePatch(context, patchPath);
            }
        });
        PatchManager.getInstance().register(new Listener() {
            ...
        });
        PatchManager.getInstance().setTag("your tag");
        PatchManager.getInstance().setChannel("");
        PatchManager.getInstance().queryAndPatch();
        originalApplication.onCreate();
    }
}
  1. 通知结果

LoadReporter

@Override
public void onLoadResult(File patchDirectory, int loadCode, long cost) {
    super.onLoadResult(patchDirectory, loadCode, cost);
    switch (loadCode) {
        case ShareConstants.ERROR_LOAD_OK:
            PatchManager.getInstance().onLoadSuccess();
            ...
            break;
        default:
            PatchManager.getInstance().onLoadFailure();
            break;
    }
    ...
}

TinkerResultService

@Override
public void onPatchResult(final PatchResult result) {
    ...
    if (result.isSuccess) {
        PatchManager.getInstance().onPatchSuccess(result.rawPatchFilePath);
    } else {
        PatchManager.getInstance().onPatchFailure(result.rawPatchFilePath);
    }
    ...
}
  1. 记得注册TinkerResultService
<service
    android:name=".tinker.SampleResultService"
    android:exported="false"/>

三、补丁调试工具(patchtool) 测试未发布的补丁,可以扫描补丁二维码,下载补丁、立即修复、重启应用

注:

参考 Readme 搭建补丁后台,不过这个后台没有push下发补丁的功能,只有后台下发后,客户端pull拉取补丁,进行补丁修复操作

如果你的app用的是bugly来作为异常上报和运营统计,那么可以直接使用bugly提供的后台,具体操作请参考 这里 , 小巫同学录制了一系列相关的视频,参考视频教程即可学会,地址在 这里 ,bugly支持push下发,比自己搭建后台更加有优势,同样,也可以使用 TinkerPatch补丁管理后台 ,不过是收费的。

参考文档:

  1. Tinker热修复集成总结
  2. Tinker 接入指南
  3. tinker官方文档
  4. tinker补丁管理后台
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,064评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,606评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,011评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,550评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,465评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,919评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,428评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,075评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,208评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,185评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,191评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,914评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,482评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,585评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,825评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,194评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,703评论 2 339