优雅实现Android Library发布到Maven私有库:动态切换+自动化配置

8 阅读4分钟

优雅实现Android Library发布到Maven私有库:动态切换+自动化配置

在Android模块化开发中,将通用Library发布到Maven私有库是提升团队协作效率的核心手段。本文将分享一套可复用、高灵活性的Maven发布方案,实现本地/远程依赖动态切换SNAPSHOT/Release版本自动分发,并提供开箱即用的脚本和完整使用指南。我们这里使用的Sonatype Nexus

一、方案核心优势

  1. 开发调试高效:一键切换本地源码依赖/Maven远程依赖,无需手动修改依赖代码
  2. 版本管理规范:自动区分SNAPSHOT(开发版)/Release(正式版)仓库,符合Maven规范
  3. 配置复用性强:通用发布脚本抽离,所有Library一键复用,降低维护成本
  4. 产物完整:自动打包AAR、源码包、文档包,生成标准POM文件(包含依赖信息)

二、整体目录结构

  • maven-publish.gradle # Maven 发布核心脚本
  • gradle.properties # 全局配置(仓库地址、版本坐标、开关)
  • settings.gradle # 仓库源配置 + 模块引入控制

三、核心配置实现

1. 全局配置(gradle.properties)

集中管理仓库地址、版本坐标、依赖切换开关,便于统一维护:

注意:这里定义的maven的groupId、artifactId、version命名的前缀必须与useLocalSource_的后缀相同,因为我们定义的方便切换的依赖方法getLibraryDependency是基于这种规则。

# ******************** Maven 仓库配置(发布/拉取) ********************
# 私服地址(Maven 仓库地址)
mavenRepoUrl = https://xxx.xxx.xxx/repository/maven-releases/
# 快照库地址(开发中版本用)
mavenSnapshotRepoUrl = https://xxx.xxx.xxx/repository/maven-snapshots/
# 仓库账号密码
mavenRepoUsername = xxx
mavenRepoPassword = xxx

# ******************** 各 Library 的 Maven 坐标 ********************
# baseLibrary 库
baselibrary_groupId  = com.xxx.baselibrary
baselibrary_artifactId = baselibrary
baselibrary_version = 1.0.0
# 快照版示例:1.0.0-SNAPSHOT

# asrlibrary 库
asrlibrary_groupId = com.xxx.asrlibrary
asrlibrary_artifactId = asrlibrary
asrlibrary_version = 1.0.0

# ******************** POM 文件通用配置 ********************
proj_name = aie Library
proj_descriptionInfo = aie 系列基础库(数据/UI)
proj_url = https://github.com/your-name/milestone
proj_licenceName = The Apache Software License, Version 2.0
proj_licenceUrl = http://www.apache.org/licenses/LICENSE-2.0.txt
proj_developerId = aie
proj_developerName = aie Dev
proj_developerEmail = dev@aie.com

# ******************** 依赖切换开关(每个 library 独立控制) ********************
useLocalSource_baselibrary = false  // true=源码依赖,false=Maven依赖
useLocalSource_asrlibrary = false   // true=源码依赖,false=Maven依赖

2. 依赖动态切换脚本(lib_common.gradle)

封装通用方法,实现本地 / 远程依赖一键切换

/**
 * 通用依赖切换方法
 * @param libName Library 名称(对应 gradle.properties 中的开关前缀,如 baselibrary)
 * @param modulePath 本地模块路径(如 :Libraries:BaseLibrary)
 * @return 对应的依赖配置
 */
ext {
    getLibraryDependency = { String libName, String modulePath ->
        // 1. 读取开关变量(是否本地源码依赖)
        def useLocal = project.hasProperty("useLocalSource_${libName}") && project.property("useLocalSource_${libName}").toBoolean()

        if (useLocal) {
            // 本地源码依赖
            return project(modulePath)
        } else {
            // Maven 依赖:拼接坐标(groupId:artifactId:version)
            def groupId = project.hasProperty("${libName}_groupId") ? project.property("${libName}_groupId") : ""
            def artifactId = project.hasProperty("${libName}_artifactId") ? project.property("${libName}_artifactId") : ""
            def version = project.hasProperty("${libName}_version") ? project.property("${libName}_version") : ""

            // 校验坐标完整性,避免报错
            if (groupId && artifactId && version) {
                return "${groupId}:${artifactId}:${version}"
            } else {
                throw new GradleException("${libName} 的 Maven 坐标配置不完整!请检查 gradle.properties 中的 ${libName}_groupId/${libName}_artifactId/${libName}_version")
            }
        }
    }
}

3. Maven 发布核心脚本(maven-publish.gradle)

实现 AAR 打包、源码 / 文档生成、仓库自动分发:

// 通用 Maven 发布配置,所有 library 复用
apply plugin: 'maven-publish'

// ******************** 生成源码包(必选) ********************
tasks.register('androidSourcesJar', Jar) {
    // 源码包命名:artifactId-version-sources.jar
    archiveClassifier.set('sources')
    // 包含 Java/Kotlin 源码
    from android.sourceSets.main.java.srcDirs
    from android.sourceSets.main.kotlin.srcDirs // 若用 Kotlin 需加
}

// ******************** Maven 发布核心配置 ********************
publishing {
    // 配置发布的仓库地址
    repositories {
        maven {
            // 区分正式版/快照版仓库
            url = uri(project.versionName.endsWith('SNAPSHOT') ?
                    rootProject.properties['mavenSnapshotRepoUrl'] :
                    rootProject.properties['mavenRepoUrl'])
            // 仓库账号密码
            credentials {
                username = rootProject.properties['mavenRepoUsername']
                password = rootProject.properties['mavenRepoPassword']
            }
        }
    }

    publications {
        mavenAndroid(MavenPublication) {
            // 从 library 模块读取专属坐标
            groupId = project.groupId
            artifactId = project.artifactId
            version = project.versionName

            // 确保 Android 任务初始化完成后再获取产物
            afterEvaluate {
                // 发布 Release 版 AAR(核心)
                artifact(tasks.getByName("bundleReleaseAar"))
                // 发布源码包(依赖方可看源码)
                artifact(androidSourcesJar)
            
            }

            // 生成 POM 文件(包含依赖、开发者、许可证等)
            pom {
                name = rootProject.properties['proj_name']
                description = rootProject.properties['proj_descriptionInfo']
                url = rootProject.properties['proj_url']

                // 许可证信息
                licenses {
                    license {
                        name = rootProject.properties['proj_licenceName']
                        url = rootProject.properties['proj_licenceUrl']
                    }
                }

                // 开发者信息
                developers {
                    developer {
                        id = rootProject.properties['proj_developerId']
                        name = rootProject.properties['proj_developerName']
                        email = rootProject.properties['proj_developerEmail']
                    }
                }

                // 自动收集依赖到 POM(避免依赖丢失)
                withXml {
                    def dependenciesNode = asNode().appendNode('dependencies')
                    // 收集 implementation 依赖
                    project.configurations.implementation.allDependencies.each { dep ->
                        if (dep.group != null && dep.name != "unspecified") {
                            def dependencyNode = dependenciesNode.appendNode('dependency')
                            dependencyNode.appendNode('groupId', dep.group)
                            dependencyNode.appendNode('artifactId', dep.name)
                            dependencyNode.appendNode('version', dep.version)
                            // 可选:添加依赖范围(默认 compile)
                            // dependencyNode.appendNode('scope', 'implementation')
                        }
                    }
                }
            }
        }
    }
}

// 发布前先构建 Release 产物
tasks.named('publishMavenAndroidPublicationToMavenRepository') {
    dependsOn(tasks.named('bundleReleaseAar'))
    dependsOn(tasks.named('androidSourcesJar'))
}

4. Library 模块配置(build.gradle)

引入通用脚本,配置专属坐标,极简接入:

plugins {
    alias(libs.plugins.android.library)
    alias(libs.plugins.kotlin.android)
    id 'kotlin-kapt'
    id 'kotlin-parcelize'
}

apply from: "${rootDir}/scripts/lib_common.gradle"

android {

    namespace 'com.aiedevice.baselib'

    defaultConfig {
        ndk {
            // 设置支持的SO库架构
            abiFilters 'armeabi-v7a'
        }
    }

}

// ******************** 配置 Maven 坐标(和 gradle.properties 对应) ********************
ext {
    groupId = baselibrary_groupId
    artifactId = baselibrary_artifactId
    versionName = baselibrary_version
}

// 引入通用发布脚本
apply from: rootProject.file('scripts/maven-publish.gradle')

dependencies {
    api libs.androidx.core.ktx

    api libs.androidx.appcompat
    api libs.androidx.constraintlayout
    api libs.androidx.material
}

5. 项目设置(settings.gradle)

控制模块引入,配合依赖开关实现源码 / 远程依赖切换:

pluginManagement {
    repositories {
        // 镜像/自定义仓库
        maven { url 'https://maven.aliyun.com/repository/google' }
        maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
        maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
        maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }

       
        gradlePluginPortal()
        google()
        mavenCentral()

        // 个人 Maven 仓库(拉取依赖用)
        maven { url = mavenRepoUrl }
        maven { url = mavenSnapshotRepoUrl }
        
        ...

    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        // 标准仓库

        // 镜像/自定义仓库
        maven { url 'https://maven.aliyun.com/repository/google' }
        maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
        maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
        google()
        mavenCentral()

        // 个人 Maven 仓库(拉取依赖用)
        maven {
            url = mavenRepoUrl
            credentials {
                username = mavenRepoUsername
                password = mavenRepoPassword
            }
        }
        maven {
            url = mavenSnapshotRepoUrl
            credentials {
                username = mavenRepoUsername
                password = mavenRepoPassword
            }
        }
        
        ...

    }
}

include ':Basic:AppUpgrade'
include ':Basic:DataCenter'
include ':Basic:Guide'
include ':Basic:Mqtt'
include ':Basic:OTA'
...

四、使用方式

1. 依赖切换(开发调试)

只需修改gradle.properties中的开关变量:

  • useLocalSource_baselibrary = true:使用本地源码依赖(调试 Library 源码)
  • useLocalSource_baselibrary = false:使用 Maven 远程依赖(集成已发布版本)

修改后同步 Gradle 即可生效,无需改动业务代码。

2. SNAPSHOT/Release 版本切换

通过版本号后缀自动区分:

  • 快照版:版本号以-SNAPSHOT结尾(如1.0.0-SNAPSHOT),自动发布到mavenSnapshotRepoUrl
  • 正式版:版本号无后缀(如1.0.0),自动发布到mavenRepoUrl

3. 发布到 Maven 私有库

方式 1:命令行执行(推荐)
# 发布指定模块(推荐,速度更快) 
./gradlew :Libraries:BaseLibrary:publish 

# 发布所有模块 
./gradlew publish 

# 仅构建不发布(验证打包是否正常) 
./gradlew :Libraries:BaseLibrary:bundleReleaseAar
方式 2:Android Studio 图形化操作
  • 打开右侧Gradle面板

  • 找到对应 Library 模块(如BaseLibrary

  • 展开Run Configurations目录

  • 双击执行publishMavenAndroidPublicationToMavenRepository任务

五、注意事项

  1. 仓库权限:确保配置的账号密码有对应仓库的发布权限

  2. 版本号规范

    • Release 版本号:主版本.次版本.修订号(如1.0.0
    • SNAPSHOT 版本号:主版本.次版本.修订号-SNAPSHOT(如1.0.0-SNAPSHOT
  3. 依赖收集:脚本自动收集implementation依赖到 POM,若需收集其他类型(如api),需修改project.configurations.implementation为对应配置

  4. 中文注释:生成 Javadoc 时若有中文注释,需确保编码为 UTF-8,避免乱码

  5. 构建环境:建议使用 Gradle 7.0+、AGP 7.0+,避免插件兼容问题