Android开发技能发展

简化Android库上传到Maven仓库的gradle配置

2021-06-01  本文已影响0人  HanlyJiang

配置 android 上传到 maven 中心仓库,发现配置的代码有点多,而且如果有多个库模块需要上传,则需要复制粘贴不少重复的配置代码,于是编写了一个 gradle 插件用于简化提取这个配置过程;

插件用途及效果

本插件用于简化Android库上传到Maven中心仓库的配置,避免每个project的gradle中都放置一份重复较多的配置;

插件有如下功能:

  1. 简化 maven-publish 插件的配置流程;
  2. 添加上传到Maven中心仓库的 publish 任务;
  3. 支持配置上传时是否包含 javadoc 及源码;

直接使用maven-publish插件的配置:

import org.gradle.api.publish.maven.MavenPom

plugins {
    id("com.android.library")
    id("signing")
    `maven-publish`
}

android {
    defaultConfig {
        versionName("1.0.0-SNAPSHOT")
    }
}

dependencies {
   // 
}

tasks.register("javadoc", Javadoc::class.java) {
    group = "publishing"
    dependsOn("assemble")
    source = android.sourceSets["main"].java.getSourceFiles()
    classpath += project.files(android.bootClasspath + File.pathSeparator)
    if (JavaVersion.current().isJava9Compatible) {
        (options as StandardJavadocDocletOptions).addBooleanOption("html5", true)
    }
    android.libraryVariants.forEach { libraryVariant ->
        classpath += libraryVariant.javaCompileProvider.get().classpath
    }
    options.apply {
        encoding("UTF-8")
        charset("UTF-8")
        isFailOnError = false

        (this as StandardJavadocDocletOptions).apply {
//            addStringOption("Xdoclint:none")
            links?.add("https://developer.android.google.cn/reference/")
            links?.add("http://docs.oracle.com/javase/8/docs/api/")
        }
    }
}

tasks.register("jarSource", Jar::class.java) {
    group = "publishing"
    from(android.sourceSets["main"].java.srcDirs)
    archiveClassifier.set("sources")
}

tasks.register("jarJavadoc", Jar::class.java) {
    group = "publishing"
    dependsOn("javadoc")
    val javadoc: Javadoc = tasks.getByName("javadoc") as Javadoc
    from(javadoc.destinationDir)
    archiveClassifier.set("javadoc")
}

fun getMyPom(): Action<in MavenPom> {
    return Action<MavenPom> {
        name.set("Android Common Utils Lib")
        description.set("Android Common Utils Library For HJ")
        url.set("https://github.com/hanlyjiang/lib_common_utils")
        licenses {
            license {
                name.set("The Apache License, Version 2.0")
                url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
            }
        }
        developers {
            developer {
                id.set("hanlyjiang")
                name.set("Hanly Jiang")
                email.set("hanlyjiang@outlook.com")
            }
        }
        scm {
            connection.set("scm:git:git://github.com/hanlyjiang/lib_common_utils.git")
            developerConnection.set("scm:git:ssh://github.com/hanlyjiang/lib_common_utils.git")
            url.set("https://github.com/hanlyjiang/lib_common_utils")
        }
    }
}


afterEvaluate {
    publishing {
        publications {
            create<MavenPublication>("release") {
                from(components.getByName("release"))
                groupId = "com.github.hanlyjiang"
                artifactId = "android_common_utils"
                version = android.defaultConfig.versionName
                pom(getMyPom())
                // 添加javadoc
                artifact(tasks.getByName("jarJavadoc") as Jar)
                // 添加source
                artifact(tasks.getByName("jarSource") as Jar)
            }
        }

        repositories {
            val ossrhCredentials = Action<PasswordCredentials> {
                username = properties["ossrhUsername"].toString()
                password = properties["ossrhPassword"].toString()
            }
            // sonar的仓库,地址根据项目的版本号来确定是snapshot还是正式仓库
            maven {
                name = "Sonartype"

                val releasesRepoUrl = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2")
                val snapshotsRepoUrl = uri("https://oss.sonatype.org/content/repositories/snapshots/")
                url = if (android.defaultConfig.versionName.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
                credentials(ossrhCredentials)
                // snapshot的地址:
                // https://oss.sonatype.org/content/repositories/snapshots/com/github/hanlyjiang/android_common_utils/
            }
            // 项目本地的仓库
            maven {
                name = "ProjectLocal"

                val releasesRepoUrl = uri(layout.buildDirectory.dir("repos/releases"))
                val snapshotsRepoUrl = uri(layout.buildDirectory.dir("repos/snapshots"))
                url = if (android.defaultConfig.versionName.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
            }
        }
    }

    signing {
        sign(publishing.publications.getByName("release"))
    }

}

使用AndroidMavenPubPlugin简化后的配置:

import org.gradle.api.publish.maven.MavenPom


plugins {
    id("com.android.library")
    id("signing")
    `maven-publish`
    // 引入我们本地仓库中的gradle插件
    id("com.github.hanlyjiang.android_maven_pub") version ("0.0.5") apply (false)
}

android {
    defaultConfig {
        versionName("1.0.1-SNAPSHOT")
    }
}

dependencies {
  // 
}

apply(plugin = "com.github.hanlyjiang.android_maven_pub")

configure<io.hanlyjiang.gradle.android.AndroidMavenPubPluginExtension> {
    groupId.set("com.github.hanlyjiang")
    artifactId.set("android-common-utils")
    projectLocalRepoPath.set("local-maven-repo")
    mavenPomAction.set(Action<MavenPom> {
        name.set("Android Common Utils Lib")
        description.set("Android Common Utils Library For HJ")
        url.set("https://github.com/hanlyjiang/lib_common_utils")
        properties.set(
            mapOf(
                "myProp" to "value",
                "prop.with.dots" to "anotherValue"
            )
        )
        licenses {
            license {
                name.set("The Apache License, Version 2.0")
                url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
            }
        }
        developers {
            developer {
                id.set("hanlyjiang")
                name.set("Hanly Jiang")
                email.set("hanlyjiang@outlook.com")
            }
        }
        scm {
            connection.set("scm:git:git://github.com/hanlyjiang/lib_common_utils.git")
            developerConnection.set("scm:git:ssh://github.com/hanlyjiang/lib_common_utils.git")
            url.set("https://github.com/hanlyjiang/lib_common_utils")
        }
    })
}

使用步骤

引入插件

在需要使用的模块的build脚本中,引入我们的插件,同时引入 maven-publish 插件和signing 插件。

kotlin.kts 脚本写法

plugins {
    id("com.android.library")
    
    // 引入signing插件
    id("signing")
    // 引入maven-publish插件
    `maven-publish`
    // 引入 android_maven_pub 插件,注意这里设置 apply 为 false,表示引入但是不应用,我们需要放在android配置段定义之后再应用
    id("com.github.hanlyjiang.android_maven_pub") version ("0.0.5") apply (false)
}

groovy 的写法

plugins {
    id 'com.android.library'
    id 'signing'
    id 'maven-publish'
    id("com.github.hanlyjiang.android_maven_pub") version("0.0.5") apply(false)
}

配置 gradle

在引入 AndroidMavenPubPlugin 插件之后,我们可以对插件进行配置。 建议将脚本转换为 kotlin dsl 的写法,能有对应的自动提示;

kotlin dsl 写法

android {
    
}

// 需要先应用插件,在android配置完成之后,建议放在脚本最下方
apply(plugin = "com.github.hanlyjiang.android_maven_pub")

configure<io.hanlyjiang.gradle.android.AndroidMavenPubPluginExtension> {
    groupId.set("com.github.hanlyjiang")
    artifactId.set("android_common_utils")
    mavenPomAction.set(Action<MavenPom> {
        name.set("Android Common Utils Lib")
        description.set("Android Common Utils Library For HJ")
        url.set("https://github.com/hanlyjiang/lib_common_utils")
        properties.set(
            mapOf(
                "myProp" to "value",
                "prop.with.dots" to "anotherValue"
            )
        )
        licenses {
            license {
                name.set("The Apache License, Version 2.0")
                url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
            }
        }
        developers {
            developer {
                id.set("hanlyjiang")
                name.set("Hanly Jiang")
                email.set("hanlyjiang@outlook.com")
            }
        }
        scm {
            connection.set("scm:git:git://github.com/hanlyjiang/lib_common_utils.git")
            developerConnection.set("scm:git:ssh://github.com/hanlyjiang/lib_common_utils.git")
            url.set("https://github.com/hanlyjiang/lib_common_utils")
        }
    })
}

groovy 脚本写法

apply plugin: "com.github.hanlyjiang.android_maven_pub"

android_maven_pub {
    groupId.set("com.github.hanlyjiang")
    artifactId.set("android-common-utils")
    mavenPomAction.set({ pom ->
        pom.with {
            name.set('HJ Android Plugin Framework')
            description.set("A Android Plugin Framework")
            url.set("https://github.com/hanlyjiang/apf-library")
            licenses {
                license {
                    name = 'The Apache Software License, Version 2.0'
                    url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                }
            }
            developers {
                developer {
                    id = 'hanlyjiang'
                    name = 'hanly jiang'
                    email = 'hanlyjiang@outlook.com'
                }
            }
            scm {
                connection = 'https://github.com/hanlyjiang/apf-library'
                developerConnection = 'https://github.com/hanlyjiang/apf-library.git'
                url = 'https://github.com/hanlyjiang/apf-library'
            }
        }
    } as Action<MavenPom>)
}

⚠️注意

  • 可以看到我们这里并没有定义版本号,版本号从 android.defaultConfig.versionName 中取 ;

  • 同时,如果版本号结尾为 -SNAPSHOT,则会发布到 snapshot 仓库,如果没有,则发布到 release 仓库;

maven仓库的属性文件配置

上传到maven center中心仓库需要进行一些账号申请和key的生成操作,可以参考Jcenter 停止服务,说一说我们的迁移方案 - InfoQ 写作平台 来完成,我们需要将最后获取到的认证信息:

按如下配置,将对应的 value 的值更改为自己的账号及 key 的对应值,然后填入到 ~/.gradle/gradle.properties 中即可

# 配置maven中心仓库访问账号
ossrhUsername=sonatype jira 账号的用户名
ossrhPassword=sonatype jira 账号的密码

# 配置签名信息
signing.keyId=公钥 ID 的后 8 位
signing.password=钥匙串的密码
signing.secretKeyRingFile=导出的 gpg 文件路径 如: /Users/hanlyjiang/.gnupg/secring.gpg 

⚠️注意: 这里的 key 是固定的,不要修改

执行上传任务

经过上面的配置,我们同步下 gradle ,对应引入了插件的项目中会生成若干任务:

在这里插入图片描述

其中:

publish 相关的任务有如下几个:

Task Name Description
publish 等于执行了下面的所有任务
publishAllPublicationsToProjectLocalRepository 将由此项目产生的所有Maven库发布到 ProjectLocal 存储库。 ProjectLocal 定义为当前项目的 build 目录的 mavenRepos 目录中,有两个子目录 snapshotsrelease, 方便查看将要发布的生成物及进行本地测试;
publishAllPublicationsToSonartypeRepository 将由此项目产生的所有 Maven 库发布到 Sonartype 存储库。
publishReleasePublicationToMavenLocal 将由此项目产生的名为release的 Maven库发布到本机 maven 缓存库。
publishReleasePublicationToProjectLocalRepository 将由此项目产生的名为release的 Maven库发布到本机 ProjectLocal 库。
publishReleasePublicationToSonartypeRepository 将由此项目产生的名为release的 Maven库发布到本机 Sonartype 库。
publishToMavenLocal 将由此项目产生的所有 Maven 库发布到 本机 maven 缓存库。

说明:

上面的任务由 maven-publish 的插件生成,该插件生成任务的规则使用 Publications 和 Repo 来组合实现,其中:

可配置项目说明

配置字段 说明 默认值
groupId maven 的 group id,需要设置为自己申请的 无默认值-必需填写
artifactId library 的 id 无默认值-必需填写
mavenPomAction 用于配置 pom 信息的字段 无默认值-必需填写
fromAndroidPubName 表示发布的 android 的 aar 的类型,releasedebug release
releasesRepoUrl maven release 仓库的上传地址 https://oss.sonatype.org/service/local/staging/deploy/maven2
snapshotsRepoUrl maven snapshots 仓库的上传地址 https://oss.sonatype.org/content/repositories/snapshots/
includeSourceJar 是否上传源码 true
includeJavadocJar 是否上传 javadoc true
projectLocalRepoPath 本地仓库的目录,相对于rootProject的目录,如:local-maven-repo 默认位于根项目的 build/mavenRepos 目录中

常见问题

发布到 snapshot

我们根据版本号来选择发布到的是 snapshot 还是 release 仓库,版本号从 android->defaultConfig - versionName 中读取,如:

android {
    compileSdkVersion(30)
    buildToolsVersion("30.0.3")

    defaultConfig {
        minSdkVersion(22)
        targetSdkVersion(30)
        versionCode(1)
        versionName("1.0.0-SNAPSHOT")
    }
}

发布到本地并进行测试

首先我们通过 projectLocalRepoPath 来改变 ProjectLocal 仓库的目录,下面的示例中将其设置为 rootProject 的 local-maven-repo 目录中

apply(plugin = "com.github.hanlyjiang.android_maven_pub")

configure<io.hanlyjiang.gradle.android.AndroidMavenPubPluginExtension> {
    groupId.set("com.github.hanlyjiang")
    artifactId.set("android-common-utils")
    projectLocalRepoPath.set("local-maven-repo")
    // ...
}

接下来我们定义本地的 Repo ,见下方名为 ProjectLocal-SnapshotsProjectLocal-Release 的仓库。

allprojects {
    repositories {
        maven { setUrl("https://maven.aliyun.com/repository/jcenter") }
        maven { setUrl("https://maven.aliyun.com/repository/google") }
        maven { setUrl("https://maven.aliyun.com/repository/gradle-plugin") }
        maven { setUrl("https://maven.aliyun.com/repository/public") }
        // 定义本地repo路径
        maven {
            name = "ProjectLocal-Snapshots"
            setUrl(File(rootProject.rootDir, "local-maven-repo${File.separator}snapshots"))
        }
        maven {
            name = "ProjectLocal-Release"
            setUrl(File(rootProject.rootDir, "local-maven-repo${File.separator}release"))
        }
        maven {
            name = "Sonatype-Snapshots"
            setUrl("https://oss.sonatype.org/content/repositories/snapshots")
//            setUrl("https://s01.oss.sonatype.org/content/repositories/snapshots")
            // snapshot可以不用用户名密码
            // 查看自己的snapshot版本: https://oss.sonatype.org/content/repositories/snapshots/com/github/hanlyjiang/
            credentials(PasswordCredentials::class.java) {
                username = property("ossrhUsername").toString()
                password = property("ossrhPassword").toString()
            }
        }
        maven {
            name = "Sonatype-Staging"
            setUrl("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
//            setUrl("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
            credentials(PasswordCredentials::class.java) {
                username = property("ossrhUsername").toString()
                password = property("ossrhPassword").toString()
            }
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读