热修复技术实践之旅——TinkerPatch热修复加Walle多渠道打包(上)

703 阅读6分钟

本文上篇导语:

本文上篇的主要内容介绍:
(1)对比当前市场上的热修复方案,对Tinker热修复方案进行了简单的介绍。
(2)详细讲解了微信Tinker的完整接入过程,文末提供了一个自己写的非常轻量的Demo,可以帮助开发者迅速实现自己项目中热修复的接入,将热修复技术运用到真实的项目中,而不仅仅是Demo测试。

希望读完本文的朋友,能对热修复及相关的技术和概念,有比较深的理解。

一、Tinker热修复方案原理简介

1、腾讯官方介绍:

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

Tinker 是一个开源项目(Github链接),它是微信官方的 Android 热补丁解决方案,它支持动态下发代码、So 库以及资源,让应用能够在不需要重新安装的情况下实现更新。

image.png

2、Tinker原理理解:

Tinker将old.apk(也就是下面要讲到的基准包,上线发布时的APK)和new.apk,进行对比,得到patch.dex,然后应用程序通过在代码中加入初始化tinker的代码,可以实现在程序运行的时候加载patch.dex(补丁文件),然后patch.dex与本机APK的classex.dex合并,生成新的classes.dex。运行时通过反射将合并后的dex文件放置在加载的dexElements数组的前面。运行时替代的原理,其实和Qzone的方案差不多,都是去反射修改dexElements。

3、为什么要使用热修复?

(1) 看看传统的App升级更新流程:

如上图,随着移动端业务复杂程度的增加,传统的APP更新流程显然无法满足业务和开发者的需求,无论是对于用户还是开发维护人员,过程过于繁琐,不够灵活。 主要存在以下几个弊端:

  • 对于开发者而言,重新发布版本代价太大。
  • 用户下载安装成本太高,可能失去耐心而直接卸载。
  • bug修复不及时,用户体验差。 (2) 再看看热修复的开发流程,明显更加灵活。
    image.png
    热修复的几大优势:
  • 无需重新发版,实时高效,开发的维护成本降低。
  • 补丁静默安装,用户无感知就能实现Bug的修复,体验好。
  • 修复成功率高,把损失降到最低。

4、为何选择使用腾讯的Tinker修复方案?

当前市面的热补丁方案有很多,其中比较出名的有阿里的 AndFix、美团的 Robust 以及 QZone 的超级补丁方案。但它们都存在无法解决的问题。其中AndFix可能接入是最简单的一个(和Tinker命令行接入方式差不多),不过兼容性还是是有一定的问题的;QZone方案对性能会有一定的影响,且在Art模式下出现内存错乱的问题;美团提出的思想方案主要是基于Instant Run的原理,兼容性比较好,但目前尚未开源。

使用Tinker的原因:

Tinker热补丁方案不仅支持类、So 以及资源的替换,它还是2.X-7.X的全平台支持。利用Tinker我们不仅可以用做 bugfix,甚至可以替代功能的发布。Tinker 已运行在微信的数亿 Android 设备上,那么为什么你不使用 Tinker 呢?

(偷偷告诉你:其实现在最好的热修复方案,是阿里2017年6月份发布的新一代非侵入式Android热修复方案——Sophix,不过本人是在去年上半年就开始使用热修复技术了,所以那会市面上的热修复技术,相较而言,Tinker是最优的选择,而且也经过了本人实际项目中的使用,所以我觉得大家在项目中如果还没有使用过热修复,那Tinker是很不错的选择,毕竟Tinker 已运行在微信的数亿 Android 设备上。对于阿里的Sophix,有兴趣的研究的朋友们,推荐大家可以去研读《Android热修复技术原理》)

二、Tinker接入步骤详解

①工程的根目录的build.gradle中配置:
// TinkerPatch 插件
    classpath "com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:1.2.6"
    compile "com.android.support:multidex:1.0.1"

②app的build.gradle中添加TinkerPatch的SDK依赖:

    compile "com.tinkerpatch.sdk:tinkerpatch-android-sdk:1.2.6"

③为了简单方便,我们将 TinkerPatch 相关的配置都放于tinkerpatch.gradle中, 我们需要在app的build.gradle中将其引入:

    apply from: 'tinkerpatch.gradle'

④tinkerpatch.gradle将其放在跟build.gradle同一级目录即可,tinkerpatch.gradle中的完整配置如下。

project-structure.png

apply plugin: 'tinkerpatch-support'

/**
 * TODO: 请按自己的需求修改为适应自己工程的参数
 */
def bakPath = file("${buildDir}/bakApk/")
def baseInfo = "app-1.0.0-0527-01-08-12"
def variantName = "release"

/**
 * 对于插件各参数的详细解析请参考
 * http://tinkerpatch.com/Docs/SDK
 */
tinkerpatchSupport {
    /** 可以在debug的时候关闭 tinkerPatch **/
    /** 当disable tinker的时候需要添加multiDexKeepProguard和proguardFiles,
     这些配置文件本身由tinkerPatch的插件自动添加,当你disable后需要手动添加
     你可以copy本示例中的proguardRules.pro和tinkerMultidexKeep.pro,
     需要你手动修改'tinker.sample.android.app'本示例的包名为你自己的包名, com.xxx前缀的包名不用修改
     **/
    tinkerEnable = true
    reflectApplication = true
    /**
     * 是否开启加固模式,只能在APK将要进行加固时使用,否则会patch失败。
     * 如果只在某个渠道使用了加固,可使用多flavors配置
     **/
    protectedApp = false
    /**
     * 实验功能
     * 补丁是否支持新增 Activity (新增Activity的exported属性必须为false)
     **/
    supportComponent = true

    autoBackupApkPath = "${bakPath}"

    appKey = "在tinkpatch管理后台创建你的应用,会有一个唯一的appkey值,填入此处即可"

    /** 注意: 若发布新的全量包, appVersion一定要更新 **/
    appVersion = "1.0.0"

    def pathPrefix = "${bakPath}/${baseInfo}/${variantName}/"
    def name = "${project.name}-${variantName}"

    baseApkFile = "${pathPrefix}/${name}.apk"
    baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt"
    baseResourceRFile = "${pathPrefix}/${name}-R.txt"

    /**
     *  若有编译多flavors需求, 可以参照: https://github.com/TinkerPatch/tinkerpatch-flavors-sample
     *  注意: 除非你不同的flavor代码是不一样的,不然建议采用zip comment或者文件方式生成渠道信息(相关工具:walle 或者 packer-ng)
     **/
}

/**
 * 用于用户在代码中判断tinkerPatch是否被使能
 */
android {
    defaultConfig {
        buildConfigField "boolean", "TINKER_ENABLE", "${tinkerpatchSupport.tinkerEnable}"
    }
}

/**
 * 一般来说,我们无需对下面的参数做任何的修改
 * 对于各参数的详细介绍请参考:
 * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
 */
tinkerPatch {
    ignoreWarning = false
    useSign = true
    dex {
        dexMode = "jar"
        pattern = ["classes*.dex"]
        loader = []
    }
    lib {
        pattern = ["lib/*/*.so"]
    }

    res {
        pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
        ignoreChange = []
        largeModSize = 100
    }

    packageConfig {
    }
    sevenZip {
        zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
//        path = "/usr/local/bin/7za"
    }
    buildConfig {
        keepDexApply = false
    }
}

⑤打出基准包。

image.png
⑥安装基准包的效果
baseapk.png
⑥模拟热修复,修改代码,打出补丁包:注意,baseApk的信息一定要和生成的基准包的路径名称匹配,否则无法成功打出补丁包。
image.png

⑦在TinkerPatch Platform,创建你的应用,发布补丁:

add_patch_version.png

realease_patch.png
watch.png

⑧点击拉取补丁,然后退出App,重新启动,查看日志,成功拉取补丁的日志及App运行效果如下图所示:

logcat.png
image.png
⑨查看后台补丁监控信息,可以看到补丁下发的情况:
image.png

到此,接入Tinker就完成了。实际项目中,咱们的应用肯定是要在各大应用市场上线的,那么肯定要打多个渠道包,按照常规,我们是采用productFlavors实现的。假设项目要打20个渠道包,那么得针对每个渠道包,分开打20个补丁包,这显然是不合理的。针对这种需求,Tinker官方给我们提供了多渠道打包的方案,如下图:

image.png

下篇我将详细讲解官方推荐的一种方案:Tinker结合walle多渠道打包方案的实现。
热修复技术实践之旅——TinkerPatch热修复加Walle多渠道打包(下)

本文完整Demo GitHub下载地址请戳:TinkerPatchDemo

本文参考:
Tinker源码
TinkerPatch 接入及平台使用文档
Android 热修复 Tinker接入及源码浅析—hongyang
MultiDex与热修复实现原理