利用 Gradle 进行 Android 包构建的大概思路说明

利用 Gradle 进行 Android 包构建的大概思路说明

问题背景

本篇要说的 Android 打包 是什么

这里所说的 Android 打包 并不是大家都知道的 ./gradlew assembleRelease 命令,而是我们一般自动化打包的时候,执行的前置和后置的一些任务,这些任务可能包括下面这些:

  • 前置任务
    • 根据某个特定文件的配置项修改一些源码内容
    • 修改本次打包的某个 gradle 编译开关
    • ……
  • 后置任务(生成 apk 文件后执行的操作)
    • 加固 apk
    • 向 apk 中写入某些特殊的内容(例如渠道信息)
    • 将 apk 上传到某个存储平台
    • 将打包的产物(例如 mapping 文件) 上传到对应平台
    • ……

其有什么问题

我所经历过的几个项目,打包的自动化都是这样子的架构:

  1. 人 :在 Jenkins 上面触发一次构件
  2. Jenkins : 拉取代码,更新项目,执行项目里的某个脚本( shell 文件或者 python 文件)。这个脚本会执行
    1. 上面所说到的 “前置任务” ,
    2. 然后执行 ./gradlew assembleRelease 之类的命令
    3. 然后获取到构建产物(例如 apk 、 mapping 文件等),执行上面所说到的 “后置任务”

这样的思路是没错的,但是因为 Android 构建日常维护人员也都是 Android 开发,上面的脚本语言(python 还好,大家都或多或少知道一些;shell 是真的让人很头大)对于很多 Android 开发来说都是写起来比较痛苦的。

除此之外,很多打包脚本代码是经年不变的,再加上随着项目增大,打包逻辑逐渐复杂,便会导致打包脚本是谁都不想去触碰的垃圾角落,一旦修改起来,十分痛苦,但是又不敢改动,只能头痛医头脚痛医脚,这也导致打包脚本代码更加恶劣了。

我简单总结了我认为使用 shell / python 的缺点,越靠前的越是重要的缺点:

  1. 语言生疏,难以理解,修改起来浪费力气;
  2. 不熟悉这些语言的第三方库,修改起来能用的方案捉襟见肘。(python 会好一些,但是相比较 java /kotlin 还是会更加熟悉一些)
  3. 脚本是弱语言类型,以及非专业的 IDE 支持,导致查看编辑起来很难懂。
  4. 和 gradle 的 build 信息是割裂的,gradle 某些选项变化的话,需要手动修改脚本里的属性。

下面会说我对此应对的方法, 在此之前,先说一下

  • 使用 Gradle 构建 Android 应用是什么?
  • 将 Android 包的构建 从 shell 或者 python 脚本改为 gradle 的优点有哪些?

使用 Gradle 构建 Android 应用是什么意思?

既然 Android 本身是使用 Gradle 打包的,Gradle 中也包含了很多相关的信息(例如产出的包的位置 / 产出的 mapping 文件位置 / 打包的 flavor / ... 等)。而且 Gradle 也支持自定义任务,那么我们为什么不直接使用 Gradle 来控制打包的流程呢?也就是说,将上面说到的前置任务和后置任务 都放入 gradle task 里执行。

这样子的话,最后的打包情景就很容易想象了。

  • 将上面的 前置任务后置任务 作为 gradle task 加入到 gradle 任务列表里。
    • 比如说, 修改应用版本号是一个 task ; 加固 apk 是一个 task。
  • 执行的时候,不再是依靠 shell 或者 python 脚本来执行这些任务,而是依靠 gradle 来执行一个 gradle 任务。

为了解耦,每个 gradle task 都只处理一件事情(类似一个功能单一的方法,有输入和输出),因为处理 apk 通常是一个很多步骤的行为,这样我们会有很多个 task ,每个 task 做一件事情。

比如说

  • 修改版本号的 task
  • 加固的 task
  • 打渠道包的 task
  • ...

但是我们通常打包的时候,希望一个动作(可能是 jenkins 上新开一个构建,也可能是一行命令行)就开始执行所有的命令。所以我们需要一个方法来按照某个顺序执行这些 gradle task 。这里有两个选择:

  • 写一个脚本,依次执行。比如说一个 shell 脚本,里面规定执行的顺序。这样的缺点是还有相当多的逻辑写在脚本里。
  • 利用 gradle 的方式组织 task 的关系。因为 gradle 会将任务按照声明的顺序进行排序并依次处理,所以这样的好处是 : 我们构建的时候声明了任务之间的依赖关系后,只需要指定执行最后一个任务就可以开始整个构建了。

这里我选择 使用 gradle 的方式组织 task 之间的依赖关系 了。

接下来说下我认为这样子相比于使用脚本进行流程控制的优点。

使用 Gradle 控制 Android 构建过程的优点

和上面列出的缺点一一对比说明。

  1. 语言生疏,难以理解,修改起来浪费力气;

    使用 Gradle 编写的话,开发使用的语言是 kotlin ,很熟悉。

    这里我选择的是 kotlin ,实际上 Groovy 也是可以的——但是 Groovy 对于大多数 Android 开发人员来说也是比较陌生的 。

  2. 不熟悉这些语言的第三方库,修改起来能用的方案捉襟见肘。(python 会好一些,但是比较起来, java /kotlin 会更加熟悉一些)

    因为使用的是 kotlin 语言,而且构建也是 gradle 进行构建的。所以 kotlin 可以使用的库,都可以使用。比如说 okhttp / gson 这些工具库。

  3. 脚本是弱语言类型,以及非专业的 IDE 支持,导致查看编辑起来很难懂。

    依旧是使用 kotlin 的优势。 在 Android Studio 中,*.gradle.kts 文件也可以支持索引和跳转,查看和开发都十分方便。

  4. 和 gradle 的 build 信息是割裂的,gradle 某些选项变化的话,需要手动修改脚本里的属性。

    因为集成到了 gradle 中,可以获得 Project 的属性,从而可以获取该次构建的所有属性。

    不过因为实际构建的 Gradle Plugin 属性是字节码生成的类,所以获取起来会有些难度,因此有一些可以获取的属性我依旧是写成了常量(因为太难获取到了)。

有以上的优点,自然也会有一些缺点。采用 Gradle 的构建方式,需要对 Gradle 的任务管理有一些了解。而很多开发同学习惯于 shell 或者 python ,一旦迁移,需要学习这些新的知识。(相对来说容易解决,因为很多 Android 开发对于 Gradle 都是有基本的了解的)

小结

其实 Gradle 的构建方式,也就是把脚本里的步骤改成 gradle 里的 task 。如果脚本里的每个步骤都是一个单独地方法的话,那就相当于把每个方法改成 task ,对应的输入和输出同样改成 Gradle task 的输入输出形式。从这个角度看,只是功能呈现形式的区别而已。

不过我认为最重要的是语言的改变。我们之前是 python 脚本控制构建流程的,那时候修改起来较为麻烦,python 虽然易学易懂,但是

  • 对于阅读,弱类型的特点还是让脚本有一定理解难度。
  • 对于修改,修改起来往往是一边搜索 python 基础的语法和 API , 一边进行修改。修改之后还很很忐忑,不确定修改的正确性。

而改成 gradle 流程和 kotlin 语言之后,因为强类型以及对 kotlin 语言的熟悉,无论是阅读还是修改都十分得心应手。

希望看了本篇短文的朋友,可以对于 Android 自动化构建多一些对比以及想法。我会在下一篇中讲述一下修改过程中碰到的问题以及比较详细的步骤。

原文地址:https://www.cnblogs.com/wkmcyz/p/15600792.html