赞
踩
在日常开发中,不可避免的需要把自己的 library 发布到 maven 仓库中,这样使用起来也更加方便。
发布 aar 包到 maven 仓库,主要是使用 Gradle 提供的插件:
maven 插件(旧版),在 Gradle 6.2 之后,就完全被弃用了(增加了 @Deprecated 注解)
maven插件,是 Gradle 1.0 的时候提供的用于发布aar/jar包到 Maven 仓库的插件。在 Gradle 1.3 中,引入了一种新的发布插件,即:maven-publish ,这个新的插件引入了一些新概念和新功能,使 Gradle 发布功能更加强大,现在是发布工件的首选选项。
POM(Project Object Model)指项目对象模型,用于描述项目构件的基本信息。一个有效的 POM 节点中主要包含一下信息:
配置 | 描述 | 举例('com.github.bumptech.glide:glide:4.11.0') |
---|---|---|
groupId | 组织 / 公司的名称 | com.github.bumptech.glide |
artifactId | 组件的名称 | glide |
version | 组件的版本 | 4.11.0 |
packaging | 打包的格式 | aar/jar |
在项目中,我们会需要依赖各种各样的二方库或三方库,这些依赖一定会存放在某个位置(Place),这个 “位置” 就叫做仓库。使用仓库可以帮助我们管理项目构件,例如 jar、aar 等等。
主流的构建工具都有三个层次的仓库概念:
构建时搜索依赖的顺序如下:
repositories
中声明的顺序依次查找。如果找到,则下载依赖文件到本地仓库,否则执行步骤 3;了解这些基本概念之后,下面就介绍一下,通过 Gradle 提供的 maven 插件 和 maven-publish 插件,如何发布aar/jar包。
maven 插件是 Gradle 1.0的时候提供的,使用文档:docs.gradle.org/4.8/usergui…
使用 maven 插件,遵循如下步骤:
在 需要发布aar包的 library 模块的 build.gradle 文件中,应用 maven 插件:
apply plugin: "maven"
在 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 是用户目录。
在指定 本地仓库地址的时候,踩了一个坑,如果想使用 本地默认仓库地址,如:
- 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 文件的。
implementation 'com.mei.http:myhttp:1.0.0-SNAPSHOT'
这是什么原因导致的呢,其实就是路径问题。
当用绝对路径的时候,即:
def localDefaultRepo = "file://" + new File(System.getProperty("user.home"), '.m2/repository').absolutePath
当仓库地址 用 localDefaultRepo 的时候,在 USER_HOME/.m2/repository/ 目录就可以看到发布之后的 aar 包了,如:
这样 aar 包就发布成功了。
使用
在 工程的 build.gradle 文件中,引入默认的本地仓库,如:
- allprojects {
- repositories {
- .....
- mavenLocal() // 使用默认的本地仓库
- }
- }
在 app 的 build.gradle 文件中,引用 myhttp 库:
- dependencies {
- .....
- implementation 'com.mei.http:myhttp:1.0.1-SNAPSHOT'
- }
这样,在 app 中就可以使用 myhttp 中的代码了。
除了使用默认的本地仓库之外,还可以指定自定义的本地仓库,即:自己指定一个目录,作为本地仓库,如:
- uploadArchives {
- repositories.mavenDeployer {
- // 配置仓库地址
- repository(url: uri('../repo'))/*{
- authentication(userName: "xxx", password: "xxx")
- }*/
-
- // 配置 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'
- }
- }
- }
通过上面的步骤,aar 基本上就发布成功了,但 aar 包中的代码,都是没有注释的,也没有源码,只是反编译看到一些代码信息,这种体验就不是很好,如:
造成这个问题的原因就是,在 上传 aar 文件的时候,没有上传源码和文档。
参考文档:docs.gradle.org/current/dsl…
创建一个 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 任务,就会上传 源码,如:
在 Android Studio 中,查看 myhttp的源码,如下:
确实是可以看到源码和注释了。
扩展:artifact
artifact
方法是提供Object
对象,具体是什么呢?主要是三种。
artifact 'my-file-name.jar'
具体的文件。artifact sourceJar
任务sourceJar
输出物,例如这里是对源码进行了打包的Jar
包。artifact source: sourceJar, classifier: 'src', extension: 'zip'
通过source
、classifier
以及extension
构造的MavenArtifact
实例,参数分别对应源文件,名称类别(artifactId-classifier
)以及扩展名称(.jar/.zip
等)。参考文档:
通过上面的操作,源码就上传到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 文档。
要想把 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"
- }
- }
- }
- }
插件类:MavenPublishPlugin
文档:
在 Gradle 6.2 之后, maven 插件就彻底被废弃,无法使用了,只能使用 maven-publish 插件,因此 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")) }
- 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:
增加上述配置之后,执行 AS 右侧的 Tasks 列表中的 publishing/publish 任务,就可以发布 aar 包。
配置了两个发版产品,debug 和 release,执行发布任务后,可以看到,在默认的 本地仓库中,确实是有正式包和测试包,如下图:
在 maven-publish 插件的基本使用中,是没有上传 aar 包的源码的,在Android Studio 中,打开类查看源码可以看到:
提示用户选择源码,这里能看到代码,是 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 包的时候,就会连同源码一起上传了,如:
从上面的代码可以看出,源码确实是上传了,注释都能看到了。
通过上面的步骤,发布的 aar 包,是不会进行依赖传递的,如:我在demo:myHttpjava 中,依赖了 OkHttp,对 myHttpjava 发布 aar 包并引用之后,在 app 工程中,无法使用 OkHttp 相关的 Api,这就是因为 依赖没有传递过来。在app 模块中:
- dependencies {
- 。。。。
-
- implementation 'com.mei.http:myhttp:1.0.4-SNAPSHOT'
- }
无法使用 OkHttp 相关的 Api,但 myHttpjava 自己写的类可以调用,如:
从上图也可以看出,OkHttp 的依赖,确实没有传递过来。
在 maven 仓库中,可以打开 .pom 文件,如图:
发现里面只有 myHttpjava 库的声明,没有其依赖库的声明,如:
这就只有 myHttpjava 的声明信息,没有依赖库的如:OkHttp的 声明信息。而 使用 maven 插件发布的 aar 包,默认是依赖传递的,如:
当然,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 文件,就可以看到依赖的库的声明信息,如图:
在app 模块中,使用 myhttp 库时,虽然还是没有手动引用 OkHttp 库,但发现已经可以使用 OkHttp 相关的 Api了。说明依赖确实得到了传递。
依赖是否传递,我们通过打印 依赖库的信息也可以看出来,如:
pom 闭包中配置的信息,最终都会保存到 .pom 文件中,如描述信息,名称,licenses,developers,scm 等,如:
- pom {
- name = "Demo"
- description = "A demonstration of Maven POM customization"
- url = "http://www.example.com/project"
- }
结果:
在配置上传源码的 任务时,基本配置如下:
- 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 包的时候,发现可以正常的访问源码了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。