发布Android包到GitHub Packages
立泉成为全职Android
开发者已有三年,在经手项目里积累了很多常用代码工具,也会在自己的业余项目中使用。但是每次都为它们重复创建Module
很繁琐,直接打包为aar
又会出现依赖问题。与jar
一样,Gradle
默认并不把外部依赖包塞进aar
,需要在使用时手动引入,否则会因为依赖缺失而无法通过编译。
为什么Gradle
使用Maven
仓库里的类库不需要手动引入依赖呢?其实,发布到Maven
仓库的aar
包同样不包含外部依赖,只不过Maven
插件会自动生成一个.pom
文件作为依赖列表,Gradle
能获取到它,然后自动下载依赖。
既然如此,是否可以把自己的包发布到Maven
仓库呢?当然可以,很多公司都会搭建内部Maven
服务器实现组件共享,Android
工程至少需要配置2个不同的Maven
仓库:
// Project 根目录的 build.gradle 文件
allprojects {
repositories {
// Google 的 Maven 仓库在阿里云的镜像
maven { url 'https://maven.aliyun.com/repository/google' }
// Maven Central 的 Maven 仓库在阿里云的镜像
maven { url 'https://maven.aliyun.com/repository/central' }
}
}
GitHub Packages
GitHub Packages
是GitHub
托管的Maven
仓库,允许将软件二进制包发布到指定的私有或公开Repository
中,相比其它仓库的优势在于能把源码和编译后的包放到同一位置。
左边是源码,右边是发布的包,一目了然,这也是我倾向它的原因。
Access token
GitHub Packages
只允许获得授权的用户发包到指定Repository
,也只有获得授权的用户才可以引入该Repository
的包作为依赖,即发包和使用包都需要授权。这里的授权,指的是一个配置权限的用户access token
,在Settings
->Developer settings
的Personal access tokens
中管理。
创建token
时,发包需要write:packages
权限,获取包需要read:packages
权限。注意必须按提示在token
创建完成后把它记录下来,一旦离开这个页面就再也看不到它了。
Publish
Maven
为Gradle
提供maven-publish
插件以支持在Gradle
中配置发包到指定Maven
仓库,同时Android Gradle plugin 3.6.0
以上已经包含对maven-publish
的支持,可以把Android
工程中定义的build variant
包发布到Maven
仓库。
// Project 根目录的 build.gradle 文件
buildscript {
ext.kotlin_version = "1.4.10"
repositories {
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/central' }
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.1'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version'
}
}
关于使用build variant
创建不同类型包的问题,通常用于打出同版本的不同渠道包,参见另一篇文章:
比如在Module
的build.gradle
中定义mi
和play
2个flavor
,用于配置发布到小米商店
和Google Play
商店的渠道包,它们与release
和debug
2个build type
组合成4个build variant
:
miRelease
miDebug
playRelease
playDebug
Module
的build.gradle
:
android {
// 2个 buildType
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
flavorDimensions 'version'
// 2个 flavor
productFlavors {
mi {
dimension = 'version'
// 定义该 flavor 的特性
...
}
play {
dimension = 'version'
// 定义该 flavor 的特性
...
}
}
}
Android Gradle plugin
会在编译时根据build variant
为用于定义Maven
发包类型的components
对象生成对应属性,来实现发布指定build variant
的包。
发包token
属于私密信息,不能上传到公开仓库,可以在工程根目录下创建一个github.properties
文件,不要push
到GitHub
上。
gpr.usr=user_name
gpr.key=your_token
配置要发包Module
的build.gradle
文件:
// 要发包的 Module 的 build.gradle
// 本 Module 是一个 Android library,打包类型为 aar
apply plugin: 'com.android.library'
// 使用 maven-publish插件
apply plugin: 'maven-publish'
// 读取 github.properties 文件里的授权信息,不要把这个文件 push 到 GitHub
def githubProperties = new Properties()
githubProperties.load(new FileInputStream(rootProject.file("github.properties")))
// 因为用于定义发布类型的 components 是在 afterEvaluate 阶段被创建的,所以必须把发布配置写在 project.afterEvaluate {} 代码块中
project.afterEvaluate {
// 定义发布
publishing {
// 定义要发布到的远程 Maven 仓库,可以定义多个,Gradle 会自动生成发送到指定 Maven 仓库的 task
repositories {
// GitHub Packages 仓库
maven {
name = "GitHubPackages"
// 指定要发布到的 GitHub Repository 仓库 url
url = uri("https://maven.pkg.github.com/apqx/JetTools")
credentials {
// 用户名和token
username = githubProperties.getProperty("gpr.usr")
password = githubProperties.getProperty("gpr.key")
}
}
}
// 定义要发布的包,可以定义多个,Gradle 会自动生成把不同的包发送到指定的 Maven 仓库的 task
publications {
// 发布 build variant 为 miRelease 的包,
myMiRelease(MavenPublication) {
// 指定要发布的 build variant,在编译阶段 components 就已经根据工程配置的 build variant 生成了对应属性
from components.miRelease
// 定义 Maven 的3个参数,可以用 [groupId:artifactId:version] 定位到该包
// 所以这3个参数的组合必须具有唯一性
groupId = 'me.mudan.tools'
artifactId = 'mi'
version = '1.0.0'
}
// 发布 build variant 为 playRelease 的包,
myPlayRelease(MavenPublication) {
from components.playRelease
groupId = 'me.mudan.tools'
artifactId = 'play'
version = '1.0.0'
}
}
}
}
执行sync
,Gradle
会根据配置的发包信息生成对应的发包task
:
publishMyMiReleasePublicationToGitHubPackagesRepository
publishMyMiReleasePublicationToMavenLocal
publishMyPlayReleasePublicationToGitHubPackagesRepository
publishMyPlayReleasePublicationToMavenLocal
执行指定task
即可把对应包发布到对应Maven
仓库,这里的MavenLocal
是本地仓库,位置在$USER_HOME/.m2/repository
目录下。
Implement
添加包作为依赖需要先在工程根目录的build.gradle
中配置其所在的Maven
仓库,必须使用有read:packages
权限的token
。
// Project 根目录的 build.gradle
buildscript {
ext.kotlin_version = "1.4.20"
repositories {
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/central' }
// 该包所在的 GitHub Repository 地址
maven { url 'https://maven.pkg.github.com/apqx/JetTools'
credentials {
username = "user_name"
password = "your_token"
}
}
}
...
}
allprojects {
repositories {
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/jcenter' }
// 该包所在的 GitHub Repository 地址
maven { url 'https://maven.pkg.github.com/apqx/JetTools'
credentials {
username = "apqx"
password = "your_token"
}
}
}
}
在要使用该包的Module
中:
// Module 的 build.gradle
dependencies {
implementation 'me.apqx.tools:play:1.0.0'
}
一些限制
不同于常见Maven
仓库,GitHub Packages
需要授权,这种限制意味着它只适合应用在小圈子里,比如团队或个人的私有类库。
还有一点是关于删除已发布的包,GitHub
允许删除私有仓库的包,但不允许删除公共仓库的包,因为可能已经有其它项目在使用。如果某个版本存在问题,正确解决方法不是删除而是发布一个修复问题的新版本。
更多信息参见Android Developers
对Maven Publish Gradle plugin的描述。