在Android开发中,通过maven仓库管理SDK的版本、上传、依赖是很常见的方式。而其中版本号的管理是很多人忽略的点。本篇以实现一个自动管理SDK上传maven版本号的gradle插件为目标,说一说其中的门道。
通过Maven管理SDK包
三类第三方库
Android开发中,我们以『包』的形式引入第三方的库,大致可以分为三类:
- 以C和C++为语言生成的动态连接库,也就是so文件
- 以Java为语言生成的归档文件,也就是Jar文件
- Android独有的包含资源文件的Android模块包,也就是aar文件
Gradle支持对maven仓库包依赖
通常除了直接导入包文件到项目中之外,Android官方提供的默认的依赖及编译工具Gradle,支持项目直接引入对Maven库中已有的资源包进行依赖。作为Android开发者而言,再熟悉不过了。例如依赖okhttp,我们可能只需要在模块的build.gradle文件中加入这么一行:
1 | implementation 'com.squareup.okhttp3:okhttp:3.12.0' |
maven插件上传SDK
对于SDK开发者而言,通常我们也是期望把自己的SDK上传到指定的maven仓库中,而客户端只需要简单的申明对SDK的某个版本的依赖,就可以了。通常情况下,我们会通过一个叫「maven」的gradle插件,来帮助我们上传SDK到maven仓库中。我们来简单回顾一下步骤:
首先是在模块的build.gralde下,申明引入『maven』插件:
1 | apply plugin: 'maven' |
假设我们的SDK包含Android的资源,也就是生成aar文件,那么我们申明如下:
1 | uploadArchives{ |
以前面说的okhttp为例,”com.squareup.okhttp3”就是groupId, “okhttp”是artifactId,版本号则是在pom.version处申明。
到这里,回顾完一个Android SDK基本的上传流程了。接下来我们来说下版本号的问题。
SDK版本号
正式版和快照版
maven仓库通常分为snapshot快照仓库和release发布仓库,snapshot快照仓库用于保存开发过程中的不稳定版本,release正式仓库则是用来保存稳定的发行版本。具体到名称,只需要在该模块的版本号后加上-SNAPSHOT即可(大写)。maven仓库的管理允许统一版本名的快照包重复更新,但是对于发布版本则不允许修改,必须升级版本号。
A.B.C
对于SDK的管理者而言,一个规范合理的版本号控制流程是很重要的。通常业内的做法是A.B.C的做法,
A代表大版本号,大版本号的变动通常意味着非常大幅度的升级,甚至可以不保证向低版本兼容。例如著名的异步流编程API,ReactiveX,1.x版本和2.x版本有相当多的改变,1.x的某些写法在2.x版本中已经是不兼容了。这个大版本号很适合做SDK的大幅重构,重大基础库的迁移等,例如从Android Support库转而采用Android-X库。
B代表中版本号,也是我们日常发版升级对应的版本号,例如1.0.0 -> 1.1.0类似如此的升级。
C代表的是小版本号,目的是在版本之间,处理因为bug修复等情况的临时版本,例如1.0.0版本发布后,发现了一个重要bug,这是可以发布1.0.1这样的版本来修复问题。
版本号管理的烦恼
那我们说,版本号的烦恼在哪里呢?根据我自己的日常开发经验,可能有这么几处:
缺少版本上传的历史记录,这一点可以说是很多SDK的痛点了,回顾某个很久之前发布的版本,忘记打tag,还原不了当时的代码情况。某个成员意外的执行上传任务,覆盖掉了原本的包,引入了包含bug甚至是编译不过的代码。
版本号的唯一性,在开发中我们通常会先集成快照包来开发和提测,在上线前某个时刻替换成正式版本。而快照包的依赖存在一些问题,例如,Android Studio的gradle缓存问题,有时候明明已经上传最新的快照包,但是IDE就是不更新。每次手动修改版本号,且不说麻烦,还没办法对应的上git提交记录。
那么有什么好办法处理这些问题呢?
自动管理版本号的Gradle插件
我这里的处理方式是自己写一个简单的gradle插件,用来自动管理SDK上传Maven仓库时的版本号。
插件功能的设计
首先我们希望每次上传新包到Maven仓库,能够记录一下上传记录,包括:上传人,上传时间,上传的版本号, 当前的分支名,最后一次提交的commit messgae等等
其次,版本名上我们希望附带两个功能,一个是指定是否为快照包,自动的在版本号后面加上-SNAPSHOT。一个是可以选择在版本号后面跟上唯一性的id。
Gradle插件的开发
Gradle插件开发
首先是如何开发Gradle插件,这里我推荐这篇文章Gradle插件开发指南
。这里我们开发一个独立的gradle插件项目,并上传到maven仓库中。
新建Gradle插件工程
Gradle插件的开发建议选择Intellij IEAD, 通过创建Gradle项目即可,语言我这里指定Groovy, 其实Java或者Kotlin甚至是Scala都是可以的。
接着设置好gradle项目需要的GroupId, ArtifactId和Version信息,创建好工程。此时工程目录结构:
其中groovy目录用来存放groovy代码,java目录存放java代码,resources目录用来声明当前项目所需的资源,后面我们介绍。其中的build.gradle文件看起来是这样:
1 | plugins { |
如果想用Android Studio来创建Gradle工程的话,可能会麻烦一些,在一个Android工程中,选择新建 Java Library module “plugin”,然后手动删除掉不需要的目录和文件,手动新建resources目录。
实现功能
首先是Gradle插件开发的模版套路,实现Plugin
1 | class AutoVersionPlugin implements Plugin<Project> { |
接着在resources文件夹下新建目录及文件:META-INF/gradle-plugins/auto_version_plugin.properties
1 | //xxx对应着AutoVersionPlugin的完整包名 |
到这一步,Gradle的壳就搭建起来了,接下来我们需要写业务逻辑:
第一步我们需要捕获maven插件的uploadArchives的task, 这里需要注意到的一点是uploadArchives属于工程的自定义task,而自定义task需要在工程的after evaluate执行完之后才能拿的到:
1 | project.afterEvaluate { |
好的,有了这一步的基础,我们接下来尝试定义gradle插件中可以用的DSL。首先我们定义DSL字段:
1 | class AutoVersion { |
对应着实际调用就类似于:
1 | autoVersion { |
接着我们只需要,在Plugin的apply方法中创建对应的DSL即可:
1 | class AutoVersionPlugin implements Plugin<Project> { |
以上我们的准备工作ok了,接下来我们需要处理两个问题,如何修改uploadArchives中的版本号参数,以及如何获取当前工程git信息。
首先是处理uploadArchives,这个借助gradle的api就可以处理
1 | //将Task uploadTask强转为Upload即可 |
其次是Git信息的获取,这里我们引入第三方库来帮助我们处理:
1 | implementation 'org.eclipse.jgit:org.eclipse.jgit:5.4.0.201906121030-r' |
这里我封装了GitInfo类,通过传入Git的路径,可以自动获取当前用户名,分支名,commit提交id和log记录等信息。
1 | class GitInfo { |
其他的部分例如如何写文件,逻辑判断等这里就不多说了。
上传插件及使用
上传方式之前就介绍过了,使用起来也很简单,在Android工程的根目录的build.gradle中,首先引入我们上传的插件:
1 | buildscript { |
其次找到需要上传的模块所在的build.gradle文件,假设这里已经引入了maven插件并配置了uploadArchives task,
我们引入我们的auto-version plugin
1 | apply plugin: 'auto_version_plugin' |
这样,上传maven任务和此前就一样,执行
1 | gradle uploadArchives |
总结
本篇介绍了如何开发一个独立的gradle插件,来协助管理maven发布的版本号和记录发布信息。不足之处,多多包涵。