Android库发布至MavenCentral流程详解
"jCenter 不久后将停止服务" 这个消息对所有 Android 开发者的影响是很大的,很多好用的第三方库都会上传到 jCenter,而且几乎所有的 Android 项目里都会依赖到 jCenter 仓库,这意味着 Android 开发者对其有很强的依赖性。作为第三方库使用者来说,一旦到了 jCenter 停服那时,只有 2 种选择:
- 使用阿里等第三方代理仓库(上面可能会缓存着这些曾经托管在 jCenter 上的第三方库)
- 祈祷第三方库作者能早日同步库到 MavenCentral
以下是其他人对 jCenter 停服事件的看法文章推荐,有兴趣可以去看看:
《浅谈 JCenter 即将被停止服务的事件》 (opens new window)
回归主题,如果你是一个第三方库开发者,且之前没有上传库到 MavenCentral 经验的话,本文可以助你早日上传库到 MavenCentral~
# 一、Sonatype 账号
# 1、注册 Sonatype
MavenCentral 和 Sonatype 的关系相当于 jCenter 和 jForg:
库平台 | 运营商 | 管理后台 |
---|---|---|
jCenter | jForg | bintray.com |
MavenCentral | Sonatype | oss.sonatype.org |
注意:最新管理后台链接是:
s01.oss.sonatype.org
,详情见https://central.sonatype.org/publish/release/ (opens new window)
所以,在上传库到 MavenCentral 之前,需要先注册登录 Sonatype,访问 https://issues.sonatype.org (opens new window) :
如果你已经有账号,则直接登录,否则点击 Sign up
进入注册页面:
填写好账号、密码、邮箱等信息即可注册成功,注册成功后再登录 Sonatype。
注意:邮箱很重要,建议是你常用的邮箱,才能及时收到 Sonatype 在 issue 中给你的答复信息提醒。
# 2、申请上传权限
现在你已经有 Sonatype 账号了,接下来理应就是借助 grdle 脚本经 管理后台(s01.oss.sonatype.org
) 把库上传到 MavenCentral ,但是,Sonatype 新用户默认是没有这个权限的,不信你可以访问 s01.oss.sonatype.org (opens new window)后,点击右上角 "Log In" 登录试试看,会提示没有权限:
注意:出现这个弹窗有 2 种可能,一种是账号密码错误,另一种没有权限。
所以,现在我们需要让 Sonatype 给我们开通这个权限,回到登录成功后跳转的那个页面issues.sonatype.org (opens new window),点击顶部的 新建
按钮,填写项目信息:
注意:图中是我个人项目信息,只是举例,不要照抄!!
这个步骤中,唯一需要注意的地方就是 Group Id
,有两种情况:
- 无网站域名:可以直接使用 github 的子域名
io.github.username
,比如我的 github 账号名是 GitLqr,那么可以填写io.github.gitlqr
。 - 有网站域名:可以填写个人或公司域名,比如:
com.gitlqr
,另外,还需要在 DNS 配置中配置一个 TXT 记录来验证域名所有权,具体请看:https://central.sonatype.org/pages/producers.html (opens new window)
注意:目前已经不能再使用
com.github
域名了,具体原因请看:https://central.sonatype.org/changelog/#2021-04-01-comgithub-is-not-supported-anymore-as-a-valid-coordinate (opens new window);使用自己的网站域名时,建议写顶级域名,不需要具体到某个项目的子域名,如此一来,Group Id
只需要申请一次,以后你的其他库都使用同一个Group Id
即可。
填写信息后,点击"新建"按钮,开启一个 issue,会显示 wait for response
,等待 Sonatype 工作人员审核回复:
因为 Sonatype 是国外运营,所以工作时间上会有时差,我们需要耐心等待 Sonatype 工作人员处理这个 issue,比如我这样:
注意:如果你想尽快上传库,那么前面已经提到了,请使用你常用的邮箱,当 Sonatype 工作人员评论时,会第一时间通过邮件通知到你。
# 二、Gradle 配置
首次向 Sonatype 申请上传权限可能会有点久,我们可以同步处理 gradle 配置。
# 1、Maven 提交脚本
在项目根目录下新建一个 maven-publish.gradle
文件,内容如下:
注意:以下内容是通用配置,直接拷贝即可。
if (project.hasProperty("android")) { // Android libraries
task sourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.srcDirs
}
task javadoc(type: Javadoc) {
// https://github.com/novoda/bintray-release/issues/71
excludes = ['**/*.kt'] // < ---- Exclude all kotlin files from javadoc file.
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
options.encoding = "utf-8"
options.charSet = "utf-8"
}
} else { // Java libraries
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.allSource
}
}
// 强制 Java/JavaDoc 等的编码为 UTF-8
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
tasks.withType(Javadoc) {
options.encoding = "UTF-8"
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
// add javadoc/source jar tasks as artifacts
artifacts {
archives javadocJar
archives sourcesJar
}
apply plugin: 'maven'
apply plugin: 'signing'
//Properties properties = new Properties()
//properties.load(project.rootProject.file('local.properties').newDataInputStream())
//
//def ossrhUsername = properties.getProperty("ossrhUsername")
//def ossrhPassword = properties.getProperty("ossrhPassword")
def PUBLISH_GROUP_ID = publishedGroupId //这里可以不是直接申请时候的groupId只要开头是就可以
def PUBLISH_ARTIFACT_ID = artifact
def PUBLISH_VERSION = libraryVersion // android.defaultConfig.versionName //这个是直接获取的库gradle里配置好的版本号,不用到处修改版本号,只需要维护一份就可以。
//签名
signing {
required { gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
repository(url: "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
snapshotRepository(url: "https://s01.oss.sonatype.org/content/repositories/snapshots/") {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
pom.groupId = PUBLISH_GROUP_ID
pom.artifactId = PUBLISH_ARTIFACT_ID
pom.version = PUBLISH_VERSION
pom.project {
packaging 'aar' //我这里发布的是安卓的包,所有写的aar
name libraryName // '发布库的简单名称'
// optionally artifactId can be defined here
description libraryDescription // '发布包的描述'
url siteUrl // '可以写公司官网地址或github个人页面地址'
scm {
connection gitUrl // 'scm:替换成项目git地址'
developerConnection gitUrl // 'scm:替换为git开头的项目地址'
url siteUrl // '项目首页,可以是github项目的主页'
}
licenses {
license {
name licenseName // 'The Apache License, Version 2.0'
url licenseUrl // 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id developerId // '这里填写申请账号时候的全名就可以'
name developerName // '这里随意填写就可以'
email developerEmail// '最好是申请账号时用的邮箱'
}
}
}
}
}
}
# 2、Maven 库信息配置
库信息共用两处配置:
- 通用信息:填写在项目根目录下的
gradle.prperties
文件中。 - Module 信息:填写在具体 Module 目录下的
build.gradle
文件中。
# 通用信息 gradle.prperties
配置
publishedGroupId=你申请权限时填写的GroupId
siteUrl=项目url
gitUrl=git链接
developerId=账号id
developerName=账号id
developerEmail=邮箱
licenseName=The Apache Software License, Version 2.0
licenseUrl=http://www.apache.org/licenses/LICENSE-2.0.txt
allLicenses=["Apache-2.0"]
具体可参考 https://github.com/GitLqr/LiteARouter/blob/main/gradle.properties (opens new window)
# Module 信息 build.gradle
配置
ext {
artifact = '库的唯一标识'
libraryName = '发布库的简单名称'
libraryDescription = '发布包的描述'
libraryVersion = '发布库的版本号'
}
具体可参考 https://github.com/GitLqr/LiteARouter/blob/main/litearouter-api/build.gradle (opens new window)
# 3、导入 Maven 脚本
在库对应的 Module 目录下的build.gradle
文件底部添加如下代码:
apply from: '../maven-publish.gradle'
在 AS 中 Sync Now
一下 Gradle,就可以在 Gradle 面板中看到多了一个 upload/uploadArchives
任务了。
# 三、GPG 签名
通过以上两步之后,还差最后一步,此时你运行 uploadArchives
任务是无法正常提交的,MavenCentral 提交还需要配置 GPG 签名文件。
# 1、使用 Gpg4win 生成 GPG 密钥
注意:以下生成 GPG 密钥的操作流程使用的是 Window 环境,如果你使用的是 Mac,请参考:https://www.jianshu.com/p/1c715203c7c6 (opens new window)
先到https://www.gpg4win.org/get-gpg4win.html (opens new window)下载安装好 Gpg4win:
启动 Gpg4win,点击 "文件" - "新建密钥对...":
创建个人 OpenPGP 密钥对:
填写账号、邮箱,并勾选 Protect the generated key with a passphrase
:
注意:在高级设置里可以设置更详细的,例如过期时间,但过期时间不可以太长,或报错。
点击 "新建", 设置密码(确认密码):
等待生成密钥后,出现如下弹窗,点击 "完成":
在主界面双击刚刚创建好的密钥,会出现如下弹窗,需要把底部的那 8 个字符复制下来,在后面的【配置密钥】环节会用到:
在主界面右击刚刚创建好的密钥,点击 "在服务器上发布...",然后一路确定:
在主界面右击刚刚创建好的密钥,点击 "导出...",这时注意了,一定要在这里先把文件名改成 "gpg" 或者 "pgp",再点保存:
注意了,Gpg4win 的导出分为两种:
"导出..."
: 导出公钥"Backup Secret Keys"
: 导出私钥
!!
提示:后续步骤需要用到的是
私钥
,所以需要点"Backup Secret Keys"
导出私钥的 gpg 文件;导出时默认文件后缀是 "asc",一定要在这时修改后缀为 "gpg",这样才能生成正确的二进制 gpg 密钥文件
。如果先保存成asc 文本文件
,再修改成 gpg 是没有用的!!!
# 2、配置密钥
这一步很关键,需要在电脑 用户目录
下的 .gradle/gradle.properties
中配置如下内容:
比如:
C:\Users\CharyLin\.gradle\gradle.properties
# MavenCentral
signing.keyId=刚才获取的密钥后8位
signing.password=之前我们执行命令时设置的密码
signing.secretKeyRingFile=刚才生成的gpg文件路径
ossrhUsername=sonatype用户名
ossrhPassword=sonatype密码
# 四、发布
# 1、上传档案(uploadArchives)
到了这一步,就可以去 Gradle 面板中双击执行 uploadArchives
任务了,成功的话,会出现如下日志:
11:12:00: Executing task 'uploadArchives'...
...
> Task :litearouter-api:sourcesJar UP-TO-DATE
> Task :litearouter-api:signArchives UP-TO-DATE
> Task :litearouter-api:uploadArchives
...
BUILD SUCCESSFUL in 16s
23 actionable tasks: 1 executed, 22 up-to-date
11:12:16: Task execution finished 'uploadArchives'.
如果出现 Unable to read secret key from file: it may not be a PGP secret key ring
的错误,有以下三种可能:
- GPG 账号、密码有误
- 使用的是 GPG 公钥文件
- 使用的是 GPG 密钥文件,但是是先保存成 asc 文件后修改成 gpg 文件
FAILURE: Build failed with an exception.
* What went wrong:
Could not evaluate onlyIf predicate for task ':litearouter-annotation:signArchives'.
> Unable to read secret key from file: D:\CharyLinDatas\GitLqr\secret.gpg (it may not be a PGP secret key ring)
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 11s
# 2、发布档案
现在可以去登录下的管理后台 https://s01.oss.sonatype.org/ (opens new window), 然后点击左侧的 "Staging Repositories":
选中刚上传的包, 底部的 Content
选项卡下,可以看到包内的文件有哪些:
确定无误后, 点击 "Close", 填写描述信息, 然后点击 "Confirm":
正常情况下, 等待几分钟后, Activity
选项卡中出现对号的 "Repository closed" 就是 close 成功了:
然后再点击 "Release" 按钮即可发布到 MavenCentral, 等待几个小时后可以在 https://search.maven.org/ (opens new window) 查询发布结果。
因为 Release 时勾选了 "Automatically Drop" 选项, 所以当包发布成功之后会自动从 Staging Repository 中删除。
需要注意的是, 如果你的库有好几个 component, 像我的项目就包含了 litearouter-annotation
、litearouter-api
、litearouter-compiler
三个, 切记一个一个按流程来, 比如:
litearouter-annotation
: upload --> close --> releaselitearouter-api
: upload --> close --> releaselitearouter-compiler
: upload --> close --> release
不要一次性全部 upload, 然后又一次性 close, 最后再一次性 release, 这种做法不确定会带来什么后果, 建议不要这样搞。
# 五、补充
# 1、Group Id
是什么
gradle 中会使用 implementation
来依赖某个第三方库,结构为:implementation GroupId:ArtifactId:Version
:
比如:
implementation 'com.android.support:appcompat-v7:27.1.1'
组成部分 | 解释 |
---|---|
GroupId | 是项目组织的唯一标识符,在实际开发中一般对应 JAVA 的包的结构,就是 main 目录里 java 的目录结构,如 ‘com.android.support’。 |
ArtifactId | 是项目的唯一标识符,在实际开发中一般对应项目的名称,就是项目根目录的名称,例:appcompat-v7。 |
Version | 是项目的版本号,例:1.0-SNAPSHOT 。其中 1.0 是版本号,SNAPSHOT 版本代表不稳定、尚处于开发中的版本。而衍生的有 Release 版本则代表稳定的版本 |
注意:这里说的是 GroupId
一般
对应包结构,也就是说,GroupId
与包名
是可以不一样的。就像 Android 项目里的applicationId
跟包名
是两个不同的概念一样。
# 参考资料
- 01
- Flutter - 子部件任意位置观察滚动数据11-24
- 02
- Flutter - 危!3.24版本苹果审核被拒!11-13
- 03
- Flutter - 轻松搞定炫酷视差(Parallax)效果09-21