安卓(Android) 发布 aar 到 maven 服务器或是本地 Repo

1. 创建修改已有代码成为安卓库项目

大家手上都或多或少有些项目可以封装成 aar 共享给其他同学。或是公司相关的库需要封装并发布给项目使用。你可以创建一个安卓 library工程,可以参考官方文档 Create an Android library
这个步骤就不在具体细讲,不是本文的主题。
更新记录:

  1. 2018-12-06:修改maven服务器账号,密码,版本号的获取方式。解决账号保护的问题,针对使用CI/CD进行发布。

2. 修改库工程的 build.gradle 文件

找到你的 library 工程的 build.gradle 文件。
下图是我的私人工程的文件目录结构,其中 audiocore 是 android library project。


打开 audiocore/build.gradle 在尾部追加如下代码:

apply plugin: 'maven-publish'

def getRepositoryUsername() {
  Properties properties = new Properties()
  properties.load(project.rootProject.file('local.properties').newInputStream())

  def MAVEN_USERNAME_LOCAL = properties.getProperty('MAVEN_USERNAME')
  return hasProperty('MAVEN_USERNAME') ? MAVEN_USERNAME : MAVEN_USERNAME_LOCAL;
}

def getRepositoryPassword() {
  Properties properties = new Properties()
  properties.load(project.rootProject.file('local.properties').newInputStream())

  def MAVEN_PASSWORD_LOCAL = properties.getProperty('MAVEN_PASSWORD')
  return hasProperty('MAVEN_PASSWORD') ? MAVEN_PASSWORD : MAVEN_PASSWORD_LOCAL
}

def getVersionName() {
  def local_version_name = "version-default-SNAPSHOT"
  if (hasProperty("VERSION_NAME")) {
      local_version_name = getProperty("VERSION_NAME")
  }
  return local_version_name
}

task androidJavadocs(type: Javadoc) {
  failOnError false
  source = android.sourceSets.main.java.srcDirs
  classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
  android.libraryVariants.all { variant ->
    if (variant.name == 'release') {
        owner.classpath += variant.javaCompile.classpath
    }
  }
  exclude '**/R.html', '**/R.*.html', '**/index.html'
}

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

task androidSourcesJar(type: Jar) {
  classifier = 'sources'
  from android.sourceSets.main.java.srcDirs
}

publishing {
  publications {
  Production(MavenPublication) {
    artifactId = POM_ARTIFACT_ID
    groupId = POM_GROUP_ID
    version = getVersionName()

    artifact bundleRelease
    artifact androidJavadocsJar
    artifact androidSourcesJar

    // The publication doesn't know about our dependencies, so we have to manually add them to the pom
    pom.withXml {

        final dependenciesNode = asNode().appendNode('dependencies')

        ext.addDependency = { Dependency dep, String scope ->
            if (dep.group == null || dep.version == null || dep.name == null || dep.name == "unspecified")
                return // ignore invalid dependencies

            final dependencyNode = dependenciesNode.appendNode('dependency')
            dependencyNode.appendNode('groupId', dep.group)
            dependencyNode.appendNode('artifactId', dep.name)
            dependencyNode.appendNode('version', dep.version)
            dependencyNode.appendNode('scope', scope)

            if (!dep.transitive) {
                // If this dependency is transitive, we should force exclude all its dependencies them from the POM
                final exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion')
                exclusionNode.appendNode('groupId', '*')
                exclusionNode.appendNode('artifactId', '*')
            } else if (!dep.properties.excludeRules.empty) {
                // Otherwise add specified exclude rules
                final exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion')
                dep.properties.excludeRules.each { ExcludeRule rule ->
                    exclusionNode.appendNode('groupId', rule.group ?: '*')
                    exclusionNode.appendNode('artifactId', rule.module ?: '*')
                }
            }
        }

        // List all "compile" dependencies (for old Gradle)
        configurations.compile.getAllDependencies().each { dep -> addDependency(dep, "compile") }
        // List all "api" dependencies (for new Gradle) as "compile" dependencies
        configurations.api.getAllDependencies().each { dep -> addDependency(dep, "compile") }
        // List all "implementation" dependencies (for new Gradle) as "runtime" dependencies
        configurations.implementation.getAllDependencies().each { dep -> addDependency(dep, "runtime") }
    }
  }

}

repositories {
  maven {
    // change URLs to point to your repos, e.g. http://my.org/repo
    //  def releasesRepoUrl = "$buildDir/repos/releases"
    //  def snapshotsRepoUrl = "$buildDir/repos/snapshots"
    // 测试阶段可以使用以上两个本地地址变量
    def releasesRepoUrl = "http://my.org/repo/movie-release" // change to your self repo url
    def snapshotsRepoUrl = "http://my.org/repo/movie-snapshot"  // change to your self snapshot repo url
    url = getVersionName().endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
    credentials {
        username getRepositoryUsername() 
        password getRepositoryPassword()
      }
    }
  }
}

使用上面代码会需要3个变量,我们将他们添加到 gradle.properties 文件里面:

POM_ARTIFACT_ID=audiocore
POM_GROUP_ID=com.javan.movie
VERSION_NAME=0.1.0.2-SNAPSHOT  // 如果发布非 SNAPSHOT快照版本的时候删除尾部版本的 -SNAPSHOT

3. 执行发布命令

打开 Gradle 控制窗口如下图:


双击执行 publish 的 task。
如果成功会出现如下日志:

4. 测试发布是否成功

在 app 工程的 build.gradle 修改依赖的写法:

dependencies {
  implementation fileTree(dir: 'libs', include: ['*.jar'])
  // implementation project(':audiocore')
  implementation "com.javan.movie:audiocore:0.1.0.2-SNAPSHOT" //修改此处的 group id、库名称、 version name
  implementation 'com.android.support:appcompat-v7:27.0.2'
}

通过下工程如何通过,说明整体已经跑通。

5. 优化配置

从上面的配置上有个缺陷,它会暴露你的 Maven 服务器的账号和密码。而且这个账号密码也不适合提交到代码仓库里面。目前有两种解决方案:

1. 将私有的Maven服务器上传权限关闭账号验证

简单说就是任何人都可以往你的私人服务器发布AAR或JAR,取消账号验证。修改配置文件

repositories {
    maven {
        // change URLs to point to your repos, e.g. http://my.org/repo
        //  def releasesRepoUrl = "$buildDir/repos/releases"
        //  def snapshotsRepoUrl = "$buildDir/repos/snapshots"
        // 测试阶段可以使用以上两个本地地址变量
        def releasesRepoUrl = "http://my.org/repo/movie-release" // change to your self repo url
        def snapshotsRepoUrl = "http://my.org/repo/movie-snapshot"  // change to your self snapshot repo url
        url = VERSION_NAME.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
        /*
        credentials {
            username '<your account name>'
            password '<your account password>'
        }*/
    }
  }
}

2. 在环境变量里面配置 Maven 账号和密码

  ➜  ~ echo "export ORG_GRADLE_PROJECT_MAVEN_PASSWORD=<your account password>" >> ~/.bash_profile
  ➜  ~ echo "export ORG_GRADLE_PROJECT_MAVEN_USERNAME=<your account name>" >> ~/.bash_profile
  ➜  source ~/.bash_profile

这个命名规则可以使得在 Gradle 编译环境变量里面出现 MAVEN_USERNAME, MAVEN_PASSWORD 这两个参数可以直接在 build.gradle 文件访问。

然后修改:

  repositories {
    maven {
        // change URLs to point to your repos, e.g. http://my.org/repo
        //  def releasesRepoUrl = "$buildDir/repos/releases"
        //  def snapshotsRepoUrl = "$buildDir/repos/snapshots"
        // 测试阶段可以使用以上两个本地地址变量
        def releasesRepoUrl = "http://my.org/repo/movie-release" // change to your self repo url
        def snapshotsRepoUrl = "http://my.org/repo/movie-snapshot"  // change to your self snapshot repo url
        url = VERSION_NAME.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
        credentials {
            username MAVEN_USERNAME
            password MAVEN_PASSWORD
        }
    }
  }
}

再重复执行一次第三的步骤。如果找不到变量,可以重新下 Android Stuido 让前面的配置生效。

总结

使用maven进行安卓库的发布,不仅减少了与项目或是其他同学私下给库带来的风险,也给库添加了版本管理。方便代码追溯。并且我们也配置了发布 sources.jar 这样配合的同学也可以调试项目的 java 层工程。方便相关同学进行错误的辅助定位。提升开发效率。当然对于一些存在保密性的代码,就不建议把 sources.jar 发布出来。

artifact bundleRelease
artifact androidJavadocsJar
// artifact androidSourcesJar // 关闭源代码文件发布。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,165评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,503评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,295评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,589评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,439评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,342评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,749评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,397评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,700评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,740评论 2 313
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,523评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,364评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,755评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,024评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,297评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,721评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,918评论 2 336

推荐阅读更多精彩内容