Android库发布至MavenCentral流程详解

欢迎关注微信公众号:FSA全栈行动 👋html

"jCenter 不久后将中止服务" 这个消息对全部 Android 开发者的影响是很大的,不少好用的第三方库都会上传到 jCenter,并且几乎全部的 Android 项目里都会依赖到 jCenter 仓库,这意味着 Android 开发者对其有很强的依赖性。做为第三方库使用者来讲,一旦到了 jCenter 停服那时,只有 2 种选择:java

  1. 使用阿里等第三方代理仓库(上面可能会缓存着这些曾经托管在 jCenter 上的第三方库)
  2. 祈祷第三方库做者能早日同步库到 MavenCentral

如下是其余人对 jCenter 停服事件的见解文章推荐,有兴趣能够去看看:
《浅谈 JCenter 即将被中止服务的事件》android

回归主题,若是你是一个第三方库开发者,且以前没有上传库到 MavenCentral 经验的话,本文能够助你早日上传库到 MavenCentral~git

1、Sonatype 帐号

一、注册 Sonatype

MavenCentral 和 Sonatype 的关系至关于 jCenter 和 jForg:github

库平台 运营商 管理后台
jCenter jForg bintray.com
MavenCentral Sonatype oss.sonatype.org

注意:最新管理后台连接是:s01.oss.sonatype.org,详情见central.sonatype.org/publish/rel…apache

因此,在上传库到 MavenCentral 以前,须要先注册登陆 Sonatype,访问 issues.sonatype.org :api

若是你已经有帐号,则直接登陆,不然点击 Sign up 进入注册页面:缓存

填写好帐号、密码、邮箱等信息便可注册成功,注册成功后再登陆 Sonatype。服务器

注意:邮箱很重要,建议是你经常使用的邮箱,才能及时收到 Sonatype 在 issue 中给你的答复信息提醒。微信

二、申请上传权限

如今你已经有 Sonatype 帐号了,接下来理应就是借助 grdle 脚本经 管理后台(s01.oss.sonatype.org) 把库上传到 MavenCentral ,可是,Sonatype 新用户默认是没有这个权限的,不信你能够访问 s01.oss.sonatype.org后,点击右上角 "Log In" 登陆试试看,会提示没有权限:

注意:出现这个弹窗有 2 种可能,一种是帐号密码错误,另外一种没有权限。

因此,如今咱们须要让 Sonatype 给咱们开通这个权限,回到登陆成功后跳转的那个页面issues.sonatype.org,点击顶部的 新建 按钮,填写项目信息:

注意:图中是我我的项目信息,只是举例,不要照抄!!

这个步骤中,惟一须要注意的地方就是 Group Id,有两种状况:

  • 无网站域名:能够直接使用 github 的子域名 io.github.username,好比个人 github 帐号名是 GitLqr,那么能够填写 io.github.gitlqr
  • 有网站域名:能够填写我的或公司域名,好比:com.gitlqr,另外,还须要在 DNS 配置中配置一个 TXT 记录来验证域名全部权,具体请看:central.sonatype.org/pages/produ…

注意:目前已经不能再使用 com.github 域名了,具体缘由请看:central.sonatype.org/changelog/#…;使用本身的网站域名时,建议写顶级域名,不须要具体到某个项目的子域名,如此一来,Group Id 只须要申请一次,之后你的其余库都使用同一个 Group Id 便可。

填写信息后,点击"新建"按钮,开启一个 issue,会显示 wait for response,等待 Sonatype 工做人员审核回复:

由于 Sonatype 是国外运营,因此工做时间上会有时差,咱们须要耐心等待 Sonatype 工做人员处理这个 issue,好比我这样:

注意:若是你想尽快上传库,那么前面已经提到了,请使用你经常使用的邮箱,当 Sonatype 工做人员评论时,会第一时间经过邮件通知到你。

2、Gradle 配置

首次向 Sonatype 申请上传权限可能会有点久,咱们能够同步处理 gradle 配置。

一、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// '最好是申请帐号时用的邮箱'
                    }
                }
            }
        }
    }
}
复制代码

二、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"]
复制代码

具体可参考 github.com/GitLqr/Lite…

Module 信息 build.gradle 配置

ext {
    artifact = '库的惟一标识'
    libraryName = '发布库的简单名称'
    libraryDescription = '发布包的描述'
    libraryVersion = '发布库的版本号'
}
复制代码

具体可参考 github.com/GitLqr/Lite…

三、导入 Maven 脚本

在库对应的 Module 目录下的build.gradle文件底部添加以下代码:

apply from: '../maven-publish.gradle'
复制代码

在 AS 中 Sync Now 一下 Gradle,就能够在 Gradle 面板中看到多了一个 upload/uploadArchives 任务了。

3、GPG 签名

经过以上两步以后,还差最后一步,此时你运行 uploadArchives 任务是没法正常提交的,MavenCentral 提交还须要配置 GPG 签名文件。

一、使用 Gpg4win 生成 GPG 密钥

注意:如下生成 GPG 密钥的操做流程使用的是 Window 环境,若是你使用的是 Mac,请参考:www.jianshu.com/p/1c715203c…

先到www.gpg4win.org/get-gpg4win…下载安装好 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 是没有用的!!!

二、配置密钥

这一步很关键,须要在电脑 用户目录 下的 .gradle/gradle.properties 中配置以下内容:

好比:C:\Users\CharyLin\.gradle\gradle.properties

# MavenCentral
signing.keyId=刚才获取的密钥后8位
signing.password=以前咱们执行命令时设置的密码
signing.secretKeyRingFile=刚才生成的gpg文件路径
ossrhUsername=sonatype用户名
ossrhPassword=sonatype密码
复制代码

4、发布

一、上传档案(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
复制代码

二、发布档案

如今能够去登陆下的管理后台 s01.oss.sonatype.org/, 而后点击左侧的 "Staging Repositories":

选中刚上传的包, 底部的 Content 选项卡下,能够看到包内的文件有哪些:

肯定无误后, 点击 "Close", 填写描述信息, 而后点击 "Confirm":

正常状况下, 等待几分钟后, Activity 选项卡中出现对号的 "Repository closed" 就是 close 成功了:

而后再点击 "Release" 按钮便可发布到 MavenCentral, 等待几个小时后能够在 search.maven.org/ 查询发布结果。

由于 Release 时勾选了 "Automatically Drop" 选项, 因此当包发布成功以后会自动从 Staging Repository 中删除。

须要注意的是, 若是你的库有好几个 component, 像个人项目就包含了 litearouter-annotationlitearouter-apilitearouter-compiler 三个, 切记一个一个按流程来, 好比:

  • litearouter-annotation: upload --> close --> release
  • litearouter-api: upload --> close --> release
  • litearouter-compiler: upload --> close --> release

不要一次性所有 upload, 而后又一次性 close, 最后再一次性 release, 这种作法不肯定会带来什么后果, 建议不要这样搞。

5、补充

一、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包名 是两个不一样的概念同样。

参考资料

若是文章对您有所帮助, 请不吝点击关注一下个人微信公众号:FSA全栈行动, 这将是对我最大的激励. 公众号不只有Android技术, 还有iOS, Python等文章, 可能有你想要了解的技能知识点哦~

相关文章
相关标签/搜索