maven插件 和 maven-publish 插件使用方法.md

截取转载自网络,方便记录知识,非原创

一、Maven插件的使用方法

maven 插件是 Gradle 1.0的时候提供的,使用 maven 插件,遵循如下步骤:

1 使用 maven 插件

在 需要发布aar包的 library 模块的 build.gradle 文件中,应用 maven 插件:

apply plugin: "maven"

2 配置POM

在 build.gradle 文件中,增加如下的 配置信息:

def localDefaultRepo = "file://" + new File(System.getProperty("user.home"), '.m2/repository').absolutePath
uploadArchives {
    repositories.mavenDeployer {
        // repository(url: uri('~/.m2/repository')) // 注释1
        // 配置仓库地址
        repository(url: uri(localDefaultRepo))
        // repository(url: uri('../repo'))
      
        // 配置 pom 信息
        pom.groupId "com.mei.http"
        pom.artifactId "myhttp"
        pom.version "1.0.0-SNAPSHOT"
        pom.packaging "aar"
      
      	// pom.project {
        //    groupId "com.mei.http"
        //    artifactId "myhttp"
        //    version "1.0.1-SNAPSHOT"
        //    packaging "aar"
        // }
    }
}

如上所示,指定 仓库的地址,配置 pom 信息。这样配置完成之后,在task 任务列表中,就可以看到 upload/uploadArchives 任务。

这里配置的仓库地址是一个本地路径,即把 aar 发布到本地的一个文件夹中。这里的 USER_HOME/.m2/repository/ 目录,是Gradle 默认的本地仓库地址,其中 USER_HOME 是用户目录。

2.1 默认本地仓库

在指定 本地仓库地址的时候,踩了一个坑,如果想使用 本地默认仓库地址,如:

repository(url: uri('/.m2/repository'))
println "path=${uri('~/.m2/repository')}"

打印出的路径: path=file:/Users/mei/WorkSpace/AndroidDemo/MAarPublish/myhttpjava/~/.m2/repository/ 执行 uploadArchives 任务之后,在 USER_HOME/.m2/repository/ 目录 中,是没有 aar 文件的, 所以这个时候,去使用的话,是加载不到 aar 文件的。

这是什么原因导致的呢,其实就是路径问题。

当用绝对路径的时候,即: def localDefaultRepo = "file://" + new File(System.getProperty("user.home"), '.m2/repository').absolutePath

当仓库地址 用 localDefaultRepo 的时候,在 USER_HOME/.m2/repository/ 目录就可以看到发布之后的 aar 包了.

这样 aar 包就发布成功了。

使用

在 工程的 build.gradle 文件中,引入默认的本地仓库,如:

allprojects {
    repositories {
        .....
        mavenLocal() // 使用默认的本地仓库
    }
}
dependencies {
		.....
    implementation 'com.mei.http:myhttp:1.0.1-SNAPSHOT'
}

这样,在 app 中就可以使用 myhttp 中的代码了。

2.2 自定义本地仓库

除了使用默认的本地仓库之外,还可以指定自定义的本地仓库,即:自己指定一个目录,作为本地仓库,如:

uploadArchives {
    repositories.mavenDeployer {
        // 配置仓库地址
        repository(url: uri('../repo'))
      
        // 配置 pom 信息
        pom.groupId "com.mei.http"
        pom.artifactId "myhttp"
        pom.version "1.0.0-SNAPSHOT"
        pom.packaging "aar"
    }
}

如:上面的路径就是在本工程的根目录下,创建一个 repo 文件夹,用于充当 本地仓库,执行 uploadArchives 任务之后,在工程的目录下就可以看到 repo 目录

在使用的时候,也是需要明确指定 本地仓库的路径,即存放该 aar 包的文件路径,与上面类似,在工程的build.gradle 文件中,增加如下代码,指明本地仓库路径:

allprojects {
    repositories {
				.....
       maven {
           url '../repo' // 如果是本工程的路径,可以直接这样显示
       }
    }
}

如果是其他的工程想要用这个库的话,则需要使用绝对路径,即:

allprojects {
    repositories {
				.....
       maven {
           url '/Users/mei/WorkSpace/AndroidDemo/MAarPublish/repo' 
       }
    }
}

3 上传源码和文档

通过上面的步骤,aar 基本上就发布成功了,但 aar 包中的代码,都是没有注释的,也没有源码,只是反编译看到一些代码信息,这种体验就不是很好

造成这个问题的原因就是,在 上传 aar 文件的时候,没有上传源码和文档。

3.1 上传源码

创建一个 Task 任务,用于上传源代码,具体如下:

task uploadSourceJar(type: Jar) {
  // 定义一个标志 (生成的jar包后面加上sources, 如: myhttp-1.0.2-20210927.115550-2-sources.jar)
    classifier = 'sources'
    println "srcDirs=${project.android.sourceSets.main.java.sourceFiles}"
  // 指定源码文件路径
    from project.android.sourceSets.main.java.sourceFiles
}

// 指定发包的时候,需要执行的task任务
artifacts {
    archives uploadSourceJar
}

增加上面的代码之后,在执行 uploadArchives 任务,就会上传 源码

扩展:artifact

artifact方法是提供Object对象,具体是什么呢?主要是三种。

  1. artifact ‘my-file-name.jar’具体的文件。
  2. artifact sourceJar任务sourceJar输出物,例如这里是对源码进行了打包的Jar包。
  3. artifact source: sourceJar, classifier: ‘src’, extension: ‘zip’通过source、classifier以及extension构造的MavenArtifact实例,参数分别对应源文件,名称类别(artifactId-classifier)以及扩展名称(.jar/.zip等)。

3.2 生成文档并上传

通过上面的操作,源码就上传到maven仓库了,在引用 aar 文件到时候,就可以看到 源码和注释信息。 如果是一个 SDK 的话,那么为了方便别人接入,还需要上传文档。

task androidJavadocs(type: Javadoc) {
    doLast {
        source = project.android.sourceSets.main.java.srcDirs
        // 需要生成 doc 的 代码路径
        classpath += project.files(project.android.getBootClasspath().join(File.pathSeparator))
        failOnError = false // javadoc 解析错误时task 不会异常停止
    }
}

// 解决 JavaDoc 中文注释生成失败的问题
tasks.withType(Javadoc) {
    options.addStringOption('Xdoclint:none', '-quiet')
    options.addStringOption('encoding', 'UTF-8')
    options.addStringOption('charSet', 'UTF-8')
}

task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
    doLast {
        //archiveFileName  The archive name. If the name has not been explicitly set
        // , the pattern for the name is:
        //[archiveBaseName]-[archiveAppendix]-[archiveVersion]-[archiveClassifier].[archiveExtension]
        // 存档名称的分类器部分,名称后面加的类别区分的名字.e.g. xxxx-javadoc.jar。
        classifier = 'javadoc'
        from androidJavadocs.destinationDir
    }
}

artifacts {
    // aar包增加注释
    archives androidJavadocsJar
}

增加如上代码,就可以生成Java 文档。

4 上传远程 Maven 仓库

要想把 aar 包上传到远程的 maven 仓库,只需要把上面的 maven 仓库地址替换成 远程maven 仓库地址就可以了,除此之外还需要增提供maven 仓库的用户名和密码,因为构建的 私有maven仓库,一般都是需要用户名和密码的。

具体如下:

uploadArchives {
    repositories.mavenDeployer {
      // 假设远程maven仓库地址为:http://10.0.192.56:8081/repository/core/
      // 账号:meiTest,密码:123456
        repository(url: "http://10.0.192.56:8081/repository/core/") {
            authentication(userName: "meiTest", password: "123456")
        }
        // 配置 pom 信息
        pom.groupId "com.mei.http"
        pom.artifactId "myhttp"
        pom.version "1.0.0-SNAPSHOT"
        pom.packaging "aar"
    }
}


如上,替换maven仓库地址,增加仓库的账号和密码,就可以上传到远程仓库中。

使用

把 aar 包上传到 maven 私有仓库时,需要校验账号和密码,在使用的的时候,同样也要校验账号和密码,如:

allprojects {
    repositories {
        ....
        // 指定私服路径和账号密码
        maven{
            url 'http://10.0.192.56:8081/repository/core/'
            credentials {
                username = "meiTest"
                password = "123456"
            }
        }
    }
}


二 maven-publish 插件

插件类:MavenPublishPlugin

在 Gradle 6.2 之后, maven 插件就彻底被废弃,无法使用了,只能使用 maven-publish 插件,因此 maven-publish 插件的使用一样也要掌握。下面就来看看具体的使用方式。

1 maven-publish插件的基本使用

apply plugin: "maven-publish"

配置发布产物:使用 maven-publish 插件发布aar 包的时候,基础 配置信息 如下:

publishing {
    // 配置maven 仓库
    repositories { RepositoryHandler handler->
        handler.mavenLocal()  // 发布到默认的 本地maven 仓库 ,路径: USER_HOME/.m2/repository/
    }
  // 配置发布产物
    publications {PublicationContainer publication->
      // 名称可以随便定义,这里定义成 maven,是因为我的 aar 包是发布到 maven 仓库的,所以这里为了见名知义,定义成了 maven
      // 任务名称:maven 
        maven(MavenPublication) {// 容器可配置的信息 MavenPublication
            // 依赖 bundleReleaseAar 任务,并上传其产出的aar
            afterEvaluate { artifact(tasks.getByName("bundleReleaseAar")) } // 方式一:生成aar包
        	  // artifact "$buildDir/outputs/aar/${project.name}-release.aar" // 方式二:指定生成的aar路径
            groupId = "com.mei.http"
            artifactId = "myhttp"
            version = "1.0.4-SNAPSHOT"
        }
    }
}


maven-publish 插件的扩展配置类是:PublishingExtension,查看 PublishingExtension 类的源码可以看到,publishing 配置,可以配置的信息有两个:

repositories:用于配置 maven 仓库地址

地址可以配置多个,在执行 publish 任务的时候,就会把 aar 包发布到所有指定的 maven 仓库地址中去。

repositories { RepositoryHandler handler ->
    handler.mavenLocal()
    handler.maven {
        url "${rootDir}/repo"
    }
    // 仓库用户名密码
    // handler.maven { MavenArtifactRepository mavenArtifactRepository ->
    //     // maven 仓库地址
    //     url 'http://10.0.192.56:8081/repository/core/'
    //     // 访问仓库的 账号和密码
    //     credentials {
    //         username = "meiTest"
    //         password = "123456"
    //     }
    // }
}


publications:配置需要发布的jar的信息,即产物aar包的信息。publications 是一个容器,类型是 PublicationContainer ,其可以配置的信息类型是 MavenPublication。即 可以理解成 publications 是一个列表集合,而集合中存储的对象是 MavenPublication,而对象的名称可以由自己随便定义。 所以 publications 也是可以配置多个的,如:

publications { PublicationContainer publicationContainer ->
  // 发布 snapshot 包
    debug(MavenPublication) {
        afterEvaluate { artifact(tasks.getByName("bundleReleaseAar")) }
        groupId = "com.mei.http"
        artifactId = "myhttp"
        version = "1.0.4-SNAPSHOT"
    }
  // 发布正式包
    release(MavenPublication) {
        afterEvaluate { artifact(tasks.getByName("bundleReleaseAar")) }
        groupId = "com.mei.http"
        artifactId = "myhttp"
        version = "1.0.4"
    }


指定上传的aar包的方式:

通过依赖生成 aar 包任务,如:afterEvaluate { artifact(tasks.getByName(“bundleReleaseAar”)) }

通过指定生成的aar 路径,如:artifact "buildDir/outputs/aar/buildDir/outputs/aar/buildDir/outputs/aar/{project.name}-release.aar"

发布aar:

增加上述配置之后,执行 AS 右侧的 Tasks 列表中的 publishing/publish 任务,就可以发布 aar 包。

配置了两个发版产品,debug 和 release,执行发布任务后,可以看到,在默认的 本地仓库中,确实是有正式包和测试包

2 上传源码

在 maven-publish 插件的基本使用中,是没有上传 aar 包的源码的,在Android Studio 中,打开类查看源码可以看到

增加上传源码的 task,如:

// 1. 增加上传源码的task
task sourceJar(type: Jar) {
    from android.sourceSets.main.java.srcDirs
    archiveClassifier = "sources"
}

publishing {
    repositories { RepositoryHandler handler ->
        handler.mavenLocal()
    }
    publications { PublicationContainer publicationContainer ->
        maven(MavenPublication) {
            artifact sourceJar // 增加上传源码的 task 
          
            afterEvaluate { artifact(tasks.getByName("bundleReleaseAar")) }
            groupId = "com.mei.http"
            artifactId = "myhttp"
            version = "1.0.5-SNAPSHOT"
        }
    }
}


增加上面的注释处的代码之后,在发布 aar 包的时候,就会连同源码一起上传了

3 依赖传递

通过上面的步骤,发布的 aar 包,是不会进行依赖传递的,如:我在demo:myHttpjava 中,依赖了 OkHttp,对 myHttpjava 发布 aar 包并引用之后,在 app 工程中,无法使用 OkHttp 相关的 Api,这就是因为 依赖没有传递过来。在app 模块中:

dependencies {
		。。。。

    implementation 'com.mei.http:myhttp:1.0.4-SNAPSHOT'
}


无法使用 OkHttp 相关的 Api,但 myHttpjava 自己写的类可以调用

当然,maven-publish 插件,对依赖传递也提供了支持。把 library 中的依赖信息,手动的添加到 pom 文件中(配置信息参考:MavenPom 类),就可以完成依赖传递了,具体如下

maven(MavenPublication) {
    // 依赖 bundleReleaseAar 任务,并上传其产出的aar
    afterEvaluate { artifact(tasks.getByName("bundleReleaseAar")) }
    artifact sourceJar
    groupId = "com.mei.http"
    artifactId = "myhttp"
    version = "1.0.6-SNAPSHOT"
    // pom文件中声明依赖,从而传递到使用方
    pom.withXml {
        def dependenciesNode = asNode().appendNode('dependencies')
        configurations.implementation.allDependencies.each {
            // 避免出现空节点或 artifactId=unspecified 的节点
            if (it.group != null && (it.name != null && "unspecified" != it.name) && it.version != null) {
                println "dependency=${it.toString()}"
                def dependencyNode = dependenciesNode.appendNode('dependency')
                dependencyNode.appendNode('groupId', it.group)
                dependencyNode.appendNode('artifactId', it.name)
                dependencyNode.appendNode('version', it.version)
                dependencyNode.appendNode('scope', 'implementation')
            }
        }
    }
}


增加上面的 pom 模块,在 发布 aar 包的时候,打开 pom 文件,就可以看到依赖的库的声明信息

4 结合 Android Gradle 插件,完成aar包的发布

在上面的步骤中,publications 闭包中的有些配置还是不够优雅的,比较繁琐,如:

配置发布的内容(即配置上传的 aar 文件),是通过如下两种方式:

依赖生成aar包的Task 任务,如:afterEvaluate { artifact(tasks.getByName(“bundleReleaseAar”)) } 通过指定生成的aar 文件路径,如:artifact "buildDir/outputs/aar/buildDir/outputs/aar/buildDir/outputs/aar/{project.name}-release.aar"

依赖传递:通过手动配置的方式,即 使用 withXml 闭包往 .pom 文件中,追加 dependency 依赖信息

具体如下:

publishing {
    // 配置maven 仓库
    repositories { RepositoryHandler handler ->
        handler.mavenLocal()
    }
    publications { PublicationContainer publicationContainer ->
        maven(MavenPublication) {
            // 依赖 bundleReleaseAar 任务,并上传其产出的aar
            afterEvaluate { artifact(tasks.getByName("bundleReleaseAar")) }
            // // 也可以指定上传的AAR包,但是需要先手动生成aar
            // artifact "$buildDir/outputs/aar/${project.name}-release.aar"
            artifact sourceJar
            groupId = "com.mei.http"
            artifactId = "myhttp"
            version = "1.0.8-SNAPSHOT"
            // pom文件中声明依赖,从而传递到使用方
            pom.withXml {
                def dependenciesNode = asNode().appendNode('dependencies')
                configurations.implementation.allDependencies.each {
                    // 避免出现空节点或 artifactId=unspecified 的节点
                    if (it.group != null && (it.name != null && "unspecified" != it.name) && it.version != null) {
                        println "dependency=${it.toString()}"
                        def dependencyNode = dependenciesNode.appendNode('dependency')
                        dependencyNode.appendNode('groupId', it.group)
                        dependencyNode.appendNode('artifactId', it.name)
                        dependencyNode.appendNode('version', it.version)
                        dependencyNode.appendNode('scope', 'implementation')
                    }
                }
            }
        }
    }
}


而在 Android 开发中,上述配置是可以简化的。具体来说就是 Android Gradle 插件对 maven-publish 插件有了支持。

但从 Android Gradle 插件 3.6.0 及更高版本(说的是这里 classpath ‘com.android.tools.build:gradle:3.6.0’)之后,也支持 maven-publish插件了,使配置可以更加简洁。

Android Gradle 插件会为应用或库模块中的每个构建变体工件创建一个组件,您可以使用它来自定义要发布到 Maven 代码库的发布内容

Android 插件所创建的组件取决于模块是否使用应用或库插件,如下表所述。

module 应该的插件 发布内容工件 组件名称
com.android.library AAR components.variant
com.android.application APK 和可用的 ProGuard 或 R8 映射文件的 ZIP components.variant_apk
com.android.application Android App Bundle (AAB)

知道这些之后,publications 的配置就变得很简单了,具体如下:

afterEvaluate {// components.release 只有在配置完成之后,才能拿到值
    publishing {
        // 配置maven 仓库
        repositories { RepositoryHandler handler ->
            handler.mavenLocal()
        }
        publications { PublicationContainer publicationContainer ->
            maven(MavenPublication) {
                from components.release // 注释1:使用 Android Gradle 插件生成的组件,作为发布的内容
                artifact sourceJar // 上传源码
                groupId = "com.mei.http"
                artifactId = "myhttp"
                version = "1.0.8-SNAPSHOT"
            }
        }
    }
}


如上所示,在注释1处:通过 from 方法,设置发布的内容,而内容是 Android Gradle 插件生成的组件。

这样指定之后,就可以正常的上传 aar 包了。并且不需要手动的添加依赖传递信息,Android Gradle 插件已经帮我们添加好了。

发布aar包 之后,查看 .pom 文件,依赖库的配置信息,也都是有的

所以,使用 Android Gradle 插件 创建的 组件,当作发布内容的时候,aar文件 和 依赖传递,都得到了解决,非常完美。

这里需要特别注意一个问题

就是 components 组件信息,只有在 Gradle 配置完成之后,才能够拿到,所以在使用的时候,需要放在 afterEvaluate 闭包内。这点一定要注意,我就是在这个地方,被坑了,开始没有放在 afterEvaluate 闭包内,所以一直找不到 release 组件

四 发布 Kotlin 项目的aar包,源码上传问题

在配置上传源码的 任务时,基本配置如下:

task sourceJar(type: Jar) {
    from android.sourceSets.main.java.srcDirs
    archiveClassifier = "sources"
}


但这种配置,如果库工程是用 Java 写的话,源码可以正常上传,但如果是 Kotlin 编写的库,发布 aar 包时,无法查看源码。

通过 android.sourceSets.main.java.srcDirs 指定的源码,只能识别到 Java 文件,而 kt 文件被忽略了,但 通过查看官方文档可以知道,from 函数是可以指定源码路径的,所以这里直接把 from 函数的参数替换为 源码路径,即:

task sourceJar(type: Jar) {
    from android.sourceSets.main.java.getSrcDirs() // 源码路径
    archiveClassifier = "sources"
}

这样在 发布 aar 包的时候,发现可以正常的访问源码了。