使用 AAR 和源 JAR 将 Android 库发布到 Maven

Posted

技术标签:

【中文标题】使用 AAR 和源 JAR 将 Android 库发布到 Maven【英文标题】:Publish an Android library to Maven with AAR and sources JAR 【发布时间】:2014-11-11 21:01:20 【问题描述】:

有人可以告诉我如何使用 maven-publish Gradle 插件发布带有 AAR 和源 jar 的 com.android.library 项目/模块吗?我可以使用旧的 maven 插件来做到这一点 - 但我想使用新的 maven-publish 插件。

【问题讨论】:

见this similar post。 【参考方案1】:

这是使用新的maven-publish 插件的示例。

apply plugin: 'maven-publish'

task sourceJar(type: Jar) 
    from android.sourceSets.main.java.srcDirs
    classifier "sources"


publishing 
    publications 
        bar(MavenPublication) 
            groupId 'com.foo'
            artifactId 'bar'
            version '0.1'
            artifact(sourceJar)
            artifact("$buildDir/outputs/aar/bar-release.aar")
        
    
    repositories 
        maven 
            url "$buildDir/repo"
        
    

使用./gradlew clean build publish发布

【讨论】:

生成的pom中好像没有记录依赖。 使用这个插件,它也会生成依赖到 pom 中:github.com/wupdigital/android-maven-publish 在我将classifier "source" 替换为classifier "sources" 后,这对我有用。 无效发布“库”:具有相同扩展名和分类器(“aar”、“null”)的多个工件。 我也收到了Invalid publication 'library': multiple artifacts with the identical extension and classifier ('aar', 'null'). 消息。 @mudit_sen,你找到解决这个问题的方法了吗?【参考方案2】:

当前答案

有了Android Gradle Plugin 7.1,现在可以很简单地做到这一点,不需要任何复杂的脚本。 AGP 现在还处理创建源和 javadocs jar。

您不需要任何单独的脚本,只需将所有内容写入模块的 build.gradle 文件即可:

plugins 
    ...
    id 'maven-publish'

android 
    ...
    publishing 
        singleVariant("release") 
            // if you don't want sources/javadoc, remove these lines
            withSourcesJar()
            withJavadocJar()
        
    

afterEvaluate 
    publishing 
        publications 
            release(MavenPublication) 
                from components.release
                groupId 'com.example'
                artifactId 'mylibrary'
                version = android.defaultConfig.versionName // or manually '1.0'
            
        
    

另见:https://developer.android.google.cn/studio/build/maven-publish-plugin


旧答案

2020 年 3 月 3 日更新:

自 Android Studio 3.6 发布以来,对构建 AAR(甚至是 APK 和 AAB)的支持已在 Android Gradle 插件 3.6.0(及更高版本)中实现。

我们不再需要处理 XML 依赖和自己的东西了。

这是我为 Android Studio 3.6.0 更新的 Gist:https://gist.github.com/Robyer/a6578e60127418b380ca133a1291f017

来自 gist 的代码:

apply plugin: 'maven-publish'

task androidJavadocs(type: Javadoc) 
    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.javaCompileProvider.get().classpath
        
    
    exclude '**/R.html', '**/R.*.html', '**/index.html'


task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) 
    archiveClassifier.set('javadoc')
    from androidJavadocs.destinationDir


task androidSourcesJar(type: Jar) 
    archiveClassifier.set('sources')
    from android.sourceSets.main.java.srcDirs


// Because the components are created only during the afterEvaluate phase, you must
// configure your publications using the afterEvaluate() lifecycle method.
afterEvaluate 
    publishing 
        publications 
            // Creates a Maven publication called "release".
            release(MavenPublication) 
                // Applies the component for the release build variant.
                from components.release

                // Adds javadocs and sources as separate jars.
                artifact androidJavadocsJar
                artifact androidSourcesJar

                // You can customize attributes of the publication here or in module's build.gradle file (if you save this as script and include it build.gradle file, then you can just replicate this whole block there only with changed fields).
                //groupId = 'com.example'
                //artifactId = 'custom-artifact'
                version = android.defaultConfig.versionName // or just '1.0'
            
        
    


旧答案:

这是我根据其他答案改进的解决方案。

要点: https://gist.github.com/Robyer/a6578e60127418b380ca133a1291f017

其他答案的变化:

已更改 classifier - 必须是 "sources"不是 "source") 处理依赖关系

还支持@aartransitive: false。在这种情况下,我们在 POM 中设置排除以忽略此依赖项的所有传递依赖项。

还支持对依赖项的自定义排除规则,例如:

  compile('com.example:something:1.0', 
      exclude group: 'com.exclude.this', module: 'some-module'
  )
不需要手动指定工件路径。

更新日志:

27.3.2018 - 在新 Gradle 中添加了对 api / implementation 依赖项的支持 23.11.2018 - 将 bundleRelease 重命名为 bundleReleaseAar,因为它在新 Gradle 中已更改(请参阅 this answer) 23.11.2018 - 将 getAllDependencies 更改为 getDependencies 以修复重复的结果条目(如我 Gist 中的 cmets 所述)。 23.04.2019 - 包裹在 project.afterEvaluate... 中,为新的 Gradle 修复它。
apply plugin: 'maven-publish'

task androidJavadocs(type: Javadoc) 
    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


project.afterEvaluate 
    publishing 
        publications 
            maven(MavenPublication) 
                //groupId 'cz.example'
                //artifactId 'custom-artifact'
                //version = android.defaultConfig.versionName
    
                artifact bundleReleaseAar
                artifact androidJavadocsJar
                artifact androidSourcesJar
    
                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.getDependencies().each  dep -> addDependency(dep, "compile") 
                    // List all "api" dependencies (for new Gradle) as "compile" dependencies
                    configurations.api.getDependencies().each  dep -> addDependency(dep, "compile") 
                    // List all "implementation" dependencies (for new Gradle) as "runtime" dependencies
                    configurations.implementation.getDependencies().each  dep -> addDependency(dep, "runtime") 
                
            
        
    

【讨论】:

def dependencyNode = dependenciesNode.appendNode('dependency') 抛出异常Could not get unknown property 'dependenciesNode' for object of type org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication. 更改了分类器 - 它必须是“来源”(而不是“来源”)对我有很大帮助! 在新的 gradle 中,bundleReleaseAar 再次抛出错误,但似乎我们只需要将整个 publishing 块包装到 project.afterEvaluate publishing ... 中。然后它再次工作。 (我刚刚更新了答案) 我刚刚更新了用于 Android Studio 3.6 的脚本。 @androiddeveloper androidJavadocs 创建 javadocs,androidJavadocsJar 从中创建一个 *.jar 文件用于发布。这可能不适用于 Kotlin 代码,但有人分叉了我的要点以添加一些支持。见那里:gist.github.com/Robyer/…【参考方案3】:

通过正确的依赖生成对 dskinners 的回答稍作调整:

apply plugin: 'maven-publish'

task sourceJar(type: Jar) 
    from android.sourceSets.main.java.srcDirs
    classifier "source"


publishing 
    publications 
        bar(MavenPublication) 
            groupId 'com.foo'
            artifactId 'bar'
            version '0.1'
            artifact(sourceJar)
            artifact("$buildDir/outputs/aar/bar-release.aar")
            pom.withXml 
                def dependenciesNode = asNode().appendNode('dependencies')
                //Iterate over the compile dependencies (we don't want the test ones), adding a <dependency> node for each
                configurations.compile.allDependencies.each 
                    if(it.group != null && (it.name != null || "unspecified".equals(it.name)) && it.version != null)
                    
                        def dependencyNode = dependenciesNode.appendNode('dependency')
                        dependencyNode.appendNode('groupId', it.group)
                        dependencyNode.appendNode('artifactId', it.name)
                        dependencyNode.appendNode('version', it.version)
                    
                
            
        
    
    repositories 
        maven 
            url "$buildDir/repo"
        
    

您可以通过定义更改versiongroupId

version = '1.0.0'
group = 'foo.bar'

【讨论】:

可以使用artifact bundleRelease,而不是硬编码aar文件名。此外,(it.name != null || "unspecified".equals(it.name)) 的条件看起来也不正确。你的意思是(it.name != null &amp;&amp; "unspecified" != it.name) 如何包含对项目模块的依赖?【参考方案4】:

如果你想避免样板代码,因为maven-publish 插件不会将依赖项写入 pom.xml

试试这个插件:android-maven-publish

publishing 
    publications 
        mavenAar(MavenPublication) 
            groupId 'com.example'
            artifactId 'mylibrary'
            version '1.0.0'
            from components.android
        
    

    repositories 
        maven 
            url "$buildDir/releases"
        
    

更新:

android-maven-publish 插件已弃用,因为 AGP 正式支持 maven-publish。

【讨论】:

感谢链接 bvarga。该插件是否负责在生成的 pom 文件中添加依赖排除项? bvarga,我刚试过,是的!感谢您让我注意到这个插件。它就像一个魅力! 这是迄今为止该问题的最佳解决方案。应该接受 IMO。 @HermanZun 查看更新后的答案。您可以在任何地方指定存储库。 android-maven-publish 插件已弃用,因为 AGP 正式支持 maven-publish。使用 AGP 3.6.0 或更高版本。【参考方案5】:

您也可以使用android maven plugin。它创建 .aar、javadoc.jar、sources.jar 和 .pom 并在将文件上传到 maven 存储库后更新 maven-metadata.xml。我也把脚本放在GitHub上。

apply plugin: 'com.android.library'
apply plugin: 'maven'

//Your android configuration
android 
    //...


//maven repository info
group = 'com.example'
version = '1.0.0'

ext 
    //Specify your maven repository url here
    repositoryUrl = 'ftp://your.maven.repository.com/maven2'
    //Or you can use 'file:\\\\C:\\Temp' or 'maven-temp' for a local maven repository


//Upload android library to maven with javadoc and android sources
configurations 
    deployerJars


//If you want to deploy to an ftp server
dependencies 
    deployerJars "org.apache.maven.wagon:wagon-ftp:2.2"


// custom tasks for creating source/javadoc jars
task javadoc(type: Javadoc) 
    source = android.sourceSets.main.java.srcDirs
    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
    destinationDir = file("../javadoc/")
    failOnError false


task javadocJar(type: Jar, dependsOn: javadoc) 
    classifier = 'javadoc'
    from javadoc.destinationDir


//Creating sources with comments
task androidSourcesJar(type: Jar) 
    classifier = 'sources'
    from android.sourceSets.main.java.srcDirs


//Put the androidSources and javadoc to the artifacts
artifacts 
    archives androidSourcesJar
    archives javadocJar


uploadArchives 
    repositories 
        mavenDeployer 
            configuration = configurations.deployerJars
            repository(url: repositoryUrl) 
                //if your repository needs authentication
                authentication(userName: "username", password: "password")
            
        
    

调用它
./gradlew uploadArchives

【讨论】:

问题的重点是使用 maven-publish 插件,而不是使用 maven 插件的旧方式。【参考方案6】:

这就是我使用 Kotlin DSL (build.gradle.kts) 包含 Dokka(查看 online)和为我的 Android Kotlin library 提供 JAR 的方式:

plugins 
    // ...
    id("org.jetbrains.dokka") version "1.4.32"
    id("maven-publish")


lateinit var sourcesArtifact: PublishArtifact
lateinit var javadocArtifact: PublishArtifact

tasks 
    val sourcesJar by creating(Jar::class) 
        archiveClassifier.set("sources")
        from(android.sourceSets["main"].java.srcDirs)
    
    val dokkaHtml by getting(org.jetbrains.dokka.gradle.DokkaTask::class)
    val javadocJar by creating(Jar::class) 
        dependsOn(dokkaHtml)
        archiveClassifier.set("javadoc")
        from(dokkaHtml.outputDirectory)
    
    artifacts 
        sourcesArtifact = archives(sourcesJar)
        javadocArtifact = archives(javadocJar)
    


publishing 
    // ...
    publications 
        create<MavenPublication>("MyPublication") 
            from(components["release"])
            artifact(sourcesArtifact)
            artifact(javadocArtifact)
            // ...
        
    

【讨论】:

以上是关于使用 AAR 和源 JAR 将 Android 库发布到 Maven的主要内容,如果未能解决你的问题,请参考以下文章

Android 库 (jar/aar) 可以用于 Xamarin 中的 iOS

Android如何将一个Demo或者app打包成aar或jar提供给其他应用使用

Android 库:使用 proguard 时释放 .aar 使 classes.jar 为空

[Android][Android Studio] *.jar 与 *.aar 的生成与*.aar导入项目方法

Android发布库(jar/aar)到MavenCentral

Android发布库(jar/aar)到MavenCentral