Android 发布开源库至 JCenter

3,154 阅读10分钟

本文 Demo 已上传至 github本文 Demo

关于 JCenter

JCenter 作为全世界最大的 Java Maven仓库,各位 Android 开发同行想必已十分了然。最新版的 AndroidStudio4.X)在新建工程时会将 JCenter 指定为默认的远程 Maven 仓库:

//Android 工程根目录的 build.gradle 脚本
buildscript {
    ...
    repositories {
        google()
        jcenter()
    }
    ...
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

JCenter 和谷歌自己的 Maven 仓库均被作为默认远程 Maven 仓库声明进了脚本。

我们工程里面用到的很多第三方依赖库,其打包的二进制文件都有被放到 JCenter 上,以便我们在工程里直接依赖。此外还有一个 MavenCentral 远程仓库也很常用,很多大型开源库的 github 主页上只放了 MavenCentral 链接。而我们一般不用在上面的 gradle 脚本里声明 MavenCentral 也能直接把对应库的二进制文件下载下来。这是因为上传到 MavenCentral 的库,上大概率也会上传到 JCenter 上。你可以简单把 JCenter 当成是 MavenCentral 的一个超集,当然这并不绝对。

JFrogBintray

说到 JCenter,就不得不提 JFrog 公司的 Bintray 网站,JCenter 其实是 Bintray 网站名下所属的一个 Maven 仓库。我们打包好的二进制文件(AAR 也好, Jar 也好),都需要先上传到 Bintray 网站,再发布到 JCenter 仓库。

Bintray网站:

bintray.com/

想要把打包好的库二进制文件发布到 JCenter,先得有个 Bintray 网站账号。对我们这些普通个人开发者而言,最合适的是注册一个免费的开源账户(也就是 Bintray 网站所谓的 OSS 账户)。像别的什么试用版账户或是企业账户这些想要把二进制包文件放到 JCenter 是要收费的!而且收费的很多功能我们作为个人开发者一般用不到,所以暂且不管。

Bintray 网站免费开源账户注册入口:

bintray.com/signup/oss

注意一定要是在这个入口注册的免费开源账户!!!不然后面我们可能无法把上传的二进制包文件发布到 JCenter (收费)

免费开源账户注册入口的页面如下:

支持使用 github 注册,还是挺方便的。我们可以从这个注册界面的左边看到免费开源账户所包含的基本功能:

What's included in the OSS?

  • Universal Support for All Package Types
  • 1 TB Downloads
  • 10 GB Storage
  • CDN

主要说了四点,也即免费开源账户支持所有的包类型,1 TB Downloads 说实话我看不懂指的是啥东西(根据我在 Bintray 网站上其他页面看到的信息,猜测指的应该是本账号下所有的二进制文件每个月 1TB 的下载流量!对个人开源而言,这个流量应该是完全够用的),共计为 10GB 的包存储空间,二进制包资源下载的 CDN 支持。

注册好以后进入网站个人主页看起来大概是这样的:

可以看到我已经有一个名叫 xiaofei 的仓库了。新注册的账号这里肯定还空空如也,不急,后面会详细说到如何新建一个 maven 仓库。

开发一个简单的工具库

为方便演示将开源库发布至 JCenter 的整个流程,这里从头开始开发一个极其简单的工具库,如 HelloWorld 那般简单,调用我们工具库中唯一的一个接口方法返回一个简单的字符串:"hello"

在 AS 里新建工程 JCenterDemo,并在工程中新建库模块 hello,最后整个工程目录结构如下:

hello 模块就是我们要打包上传到 JCenter 上面的工具库,其中只有个非常简单的获取字符串 "hello" 的静态方法:

public class HelloApi {
    public static String hello(){
        return "hello";
    }
}

然后 app 模块里调用了这个工具方法将字符串显示到了主界面上。很简单一个东西,其他也没啥可说的,主要为了下面的演示使用,完整代码详见上面提到的 Demo

上传 Bintray 配置

然后在我们本地工程目录里面做些必要的配置和依赖为把 hello 库打包上传至 Bintray 做好准备。

首先我们在工程根目录的 build.gradle 文件中添加跟上传 Bintray 相关的必要 gradle 插件的依赖路径:

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.0.2"

        //bintray 插件
        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
        classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
    }
}

看到上面那两行关键的 classpath 声明了吧!接下来我们需要给 hello 模块写一下打包和上传的必要脚本,当然不用我们自己从头写,已经有人帮我们写好并分享到 github 上面了,把我们的代码打包和上传至 JCenter 的开源 gradle 脚本所在的 url 分别为:

//打包脚本
https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle
//上传脚本
https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle

如果在我们的工程里直接 apply 这两个在线的 gradle 脚本会缺少灵活性,因为我们无法根据自己的需求定制在线脚本里的内容,而且可能还会因为网络的原因两个脚本下载不下来!所有我们把这两个脚本以离线的形式放到我们工程目录本地里面。当然我已经在 demo 里做好离线了,详见 demo 工程根目录里面的 installv1.gradlebintrayv1.gradle 脚本。

其中用于打包生成待上传二进制文件的 gradle 脚本 installv1.gradle 内容如下:

apply plugin: 'com.github.dcendents.android-maven'

group = publishedGroupId                               // Maven Group ID for the artifact

install {
    repositories.mavenInstaller {
        // This generates POM.xml with proper parameters
        pom {
            project {
                packaging 'aar'
                groupId publishedGroupId
                artifactId artifact

                // Add your description here
                name libraryName
                description libraryDescription
                url siteUrl

                // Set your license
                licenses {
                    license {
                        name licenseName
                        url licenseUrl
                    }
                }
                developers {
                    developer {
                        id developerId
                        name developerName
                        email developerEmail
                    }
                }
                scm {
                    connection gitUrl
                    developerConnection gitUrl
                    url siteUrl

                }
            }
        }
    }
}

用于把我们打包好的文件上传到 Bintray 的脚本 installv1.gradle 内容如下:

apply plugin: 'com.jfrog.bintray'

version = libraryVersion

if (project.hasProperty("android")) { // Android libraries
    task sourcesJar(type: Jar) {
        classifier = 'sources'
        from android.sourceSets.main.java.srcDirs
    }

//    task javadoc(type: Javadoc) {
//        source = android.sourceSets.main.java.srcDirs
//        classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
//    }
} else { // Java libraries
    task sourcesJar(type: Jar, dependsOn: classes) {
        classifier = 'sources'
        from sourceSets.main.allSource
    }
}

//task javadocJar(type: Jar, dependsOn: javadoc) {
//    classifier = 'javadoc'
//    from javadoc.destinationDir
//}

artifacts {
//    archives javadocJar
    archives sourcesJar
}

// Bintray
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())

bintray {
    user = properties.getProperty("bintray.user")
    key = properties.getProperty("bintray.apikey")

    configurations = ['archives']
    pkg {
        repo = bintrayRepo
        name = bintrayName
        desc = libraryDescription
        websiteUrl = siteUrl
        vcsUrl = gitUrl
        licenses = allLicenses
        publish = true
        publicDownloadNumbers = true
        version {
            desc = libraryDescription
            gpg {
                sign = true //Determines whether to GPG sign the files. The default is false
                passphrase = properties.getProperty("bintray.gpg.password")
                //Optional. The passphrase for GPG signing'
            }
        }
    }
}

这两个脚本的内容暂不做详细分析,下面我们直接在 hello 模块的 build.gradle 脚本里应用上面两个脚本文件:

apply plugin: 'com.android.library'

android {
    ...

    defaultConfig {
        ...
    }

    buildTypes {
        ...
    }
}

dependencies {
    ...
}

apply from: '../installv1.gradle'
apply from: '../bintrayv1.gradle'

关键就在最后两行代码,然后同步 gradle,这时候应该会直接报错,说是找不到新写脚本里的一些属性。这时就需要我们声明好两个脚本里用到的一些 ext 扩展属性了,在工程的根目录里新建一个 bintray-config.gradle 文件,对于我写的 demo,其内容如下:

ext {
    //bintray 网站上你创建的仓库的名字(必配项),其值需与自己在 JCenter 上创建的 Repository 的名字一致
    bintrayRepo = 'repo'
    //在这个仓库下的 package name(必配项),其值需与自己在 JCenter 上创建的 Repository 下的 package 的名字一致
    bintrayName = 'hello'
    //以上两项是必配的指向 Bintray 网站上你的仓库名和仓库下的 package 名

    //以下三项均为必配项
    //publishedGroupId:artifact:libraryVersion 构成你开源库的唯一路径
    //对本 demo:com.github.xiaofeidev:hello:1.0.0,在模块的build.gradle里就可以根据这个路径来 implementation 了
    publishedGroupId = 'com.github.xiaofeidev'
    artifact = 'hello'
    libraryVersion = "1.0.0"

    //对开源库的描述描述信息
    libraryName = 'hello'
    libraryDescription = 'JCenter Demo 演示'
    siteUrl = 'https://github.com/xiaofei-dev/JCenterDemo/'

    //开源库对应的 github 地址
    gitUrl = 'https://github.com/xiaofei-dev/JCenterDemo.git'

    //开发者信息
    developerId = 'xiaofei_dev'
    developerName = 'xiaofei_dev'
    developerEmail = 'xiaofei.dev@gmail.com'

    //开源协议
    licenseName = 'The Apache Software License, Version 2.0'
    licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
    allLicenses = ["Apache-2.0"]

    //打包&上传用到的 gradle 命令
//    gradlew install
//    gradlew bintrayUpload

//    gradlew install 用于将项目编译、打包生成 pom,aar 等文件;
//    gradlew bintrayUpload 用于将生成的 pom,aar 等文件上传至 bintray 仓库中;
}

其中每个属性字段定义的是啥东西结合注释应该已经很清楚了!其中有五个属性非常重要:

  • bintrayRepo(Bintray 网站上你创建的仓库 Repository 的名字,对我写的 demo 而言,仓库名就叫 repo,详见下文建仓库部分)
  • bintrayName(在这个仓库下创建的 packagepackage name,对我写的 demo 而言,包名是 hello,详见下文的建 package 部分)
  • publishedGroupId(publishedGroupId:artifact:libraryVersion 构成开源库唯一的依赖路径,见上文代码注释)
  • artifact(publishedGroupId:artifact:libraryVersion 构成开源库唯一的依赖路径,见上文代码注释)
  • libraryVersion(publishedGroupId:artifact:libraryVersion 构成开源库唯一的依赖路径,见上文代码注释)

具体含义结合代码注释和括号里的说明应该不难理解。

这个 bintray-config.gradle 脚本文件也是你以后发布自己的开源库需要专门定制的部分。下面重点来看下 hello 模块的 build.gradle 脚本的最终内容:

apply plugin: 'com.android.library'

android {
    ...

    defaultConfig {
        ...
    }

    buildTypes {
        ...
    }
}

dependencies {
    ...
}

apply from: '../bintray-config.gradle'
apply from: '../installv1.gradle'
apply from: '../bintrayv1.gradle'

注意最下面的三行。

这时我们的工程项目已经可以顺利运行到手机上了。下面我们来进行最终的步骤,把我们要发布的库(这里主要是 hello 模块)打包(这里主要是一个 AAR 文件)并上传到 Bintray 上面。

通过 Bintray上传到 JCenter 仓库

经过上面的步骤,一切准备工作都已就绪,现在我们回到一开始说到的 Bintray 网站并登录我们注册的账号。

这时我们的个人主页上应该显示我们还没有任何个人的 Repository,我们得新建一个 Repository 仓库。这时点击页面的

Add New Repository

按钮,然后来到新建仓库的页面:

我这里把仓库命名为 repo,类型选择 Maven,最终各选项填写如下:

然后点 Create 按钮这个仓库就创建成功了。然后进入这个刚创建的仓库,点击 Add New Package 按钮开始创建一个 package。创建 package 的详情页填写如下:

注意带星号的都是必填项,然后点创建按钮,就创建好了一个 package

OK 到这里我们还差最后一步配置了,回到我们上面的 installv1.gradle 脚本发现里面有这样两行代码:

...
bintray {
    user = properties.getProperty("bintray.user")
    key = properties.getProperty("bintray.apikey")
    ...
}

其中 user 和 key 这两个字段就是我们等会儿把打包的文件上传到 Bintray 网站上面必需提供的用户名和 apikey。这两个字段的值放在我们工程的 local.properties 文件里最合适不过了,这样配置正确的 .gitignore 文件会在版本控制(Git 而言)中忽略掉这个文件从而避免把我们私人的敏感信息给开源出去!

关于这两个字段的含义,user 是你在 Bintray 网站上的用户名,例如我在 Bintray 网站上的用户名是 xiaofei00

key 是你在 Bintray 网站上的 apikey,其查看方式如下:

最后 local.properties 文件的内容大概如下:

...
bintray.user= xiaofei00
bintray.apikey= xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

然后从命令行里执行两条基本命令,先打包再上传,我们一步一步来。

从 AS 里打开命令行然后执行打包命令:

gradlew install

打包成功后大概会输出如下内容:

我们工程中关于上传 Bintray 网站的配置都已就绪,紧接着再执行上传到 Bintray 网站的命令:

gradlew bintrayUpload

如果命令执行成功,本文的 demo 会有以下输出:

这就表示我们刚才打包的 hello 库的相关二进制文件和描述信息已经成功上传到了 Bintray 网站上面。距离大功告成还差最后一步了,这时打开 Bintray 网站上我们刚建好的 repo 仓库的 hello 包的页面,然后点这个 Go to Old Look 按钮:

然后点击右下角的 Add to JCenter按钮:

再然后:

点右下角的 send 按钮,提交给 Bintray 网站审核一下。

如果我们上面所做工作都没问题的话,过大概两三个小时,就会收到 Bintray 网站的发布到 JCenter 仓库成功的站内信息:

这时让 app 模块外部依赖下我们刚发布的 hello 库:

//app 模块的 build.gradle 脚本
...
android {
    ...
}

dependencies {
    ...
	//注释掉本地模块依赖
    //implementation project(path: ':hello')

    implementation 'com.github.xiaofeidev:hello:1.0.0'
}

然后 syncgradle,大功告成~

此外我还有一个长期维护的 Android 圆角布局开源库: RoundWidget

此项目也比较简单,源码里有更完整的 JCenter 上传示例,方便大家参考。