Android Studio Gradle 未使用传递依赖项

Android Studio Gradle not using transitive dependencies

在一个单独的项目中遇到这个问题,我做了一个测试项目来验证这个问题。

  1. 打开Android Studio 3.0,我创建了一个新的基础项目。它的主要模块叫做 app.
  2. 我添加了一个库模块,名为 libraryone
  3. libraryone中,我添加了对Gson的依赖,并使用Gson添加了一个class和一个静态方法。
  4. app中,我添加了对libraryone的依赖。我和 transitive = true 一起简单地测试了它,因为互联网似乎模糊地同意这样做可能会有所帮助。 (它没有。)
  5. app中,在MainActivity中,我使用了libraryone中的class。这有效(只要我没有设置 transitive = false 即可)。
  6. 但是,我无法从 MainActivity 本身引用 Gson。它给出了 "Cannot resolve symbol 'Gson'" 错误,来自 Android Studio 以及来自命令行 Gradle。这……不寻常且令人恼火,因为这意味着您必须在一组项目中重复所有的公共依赖项,并且 classes 无论如何都包含在输出中。肯定是我做错了什么,或者这是一个错误?

我的代码如下:

MainActivity.java(在app

package com.erhannis.test.dependencytest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import com.erhannis.test.libraryone.LibClass;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String s = LibClass.convert(new Object());
        System.out.println("out: " + s);

        new com.google.gson.Gson(); // This line gives a compile-time error: "Cannot resolve symbol 'Gson'"
    }
}

LibClass.java(在libraryone

package com.erhannis.test.libraryone;

import com.google.gson.Gson;

public class LibClass {
    public static String convert(Object o) {
        Gson gson = new Gson();
        return gson.toJson(o);
    }
}

build.gradle (app)

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.erhannis.test.dependencytest"
        minSdkVersion 18
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'

    implementation (project(path: ':libraryone'))
}

build.gradle (libraryone)

apply plugin: 'com.android.library'

android {
    compileSdkVersion 26
    defaultConfig {
        minSdkVersion 18
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'com.android.support:appcompat-v7:26.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'

    implementation 'com.google.code.gson:gson:2.8.2'
}

因为你把传递依赖声明为implementation。使用 api 而不是 implementation 更改:

implementation 'com.google.code.gson:gson:2.8.2'

至:

api 'com.google.code.gson:gson:2.8.2'

可以找到你应该在你的库中声明传递依赖的原因here

您正在使用:

implementation (project(path: ':libraryone'))
implementation 'com.google.code.gson:gson:2.8.2'

勾选 official doc:

When your module configures an implementation dependency, it's letting Gradle know that the module does not want to leak the dependency to other modules at compile time. That is, the dependency is available to other modules only at runtime.

在这里你可以找到非常good blog关于它的。

最好的解决方案是为 aar 依赖创建一个私有 Github/Gitlab 存储库。如果需要,您也可以将其设置为 public。 aar 文件在本地使用时不包含传递依赖。我们需要使用 .pom 文件将其发布到某处。方法如下,

  1. 在 Github
  2. 中创建个人访问令牌

Github -> Settings -> Developer Settings -> Personal Access token - with write:packages / read:packages access level
(create two tokens if you need to distribute privately

  1. id 'maven-publish'添加到模块级别的插件gradle

  2. 将以下代码添加到模块级别 gradle

    task sourceJar(type: Jar) {
        from android.sourceSets.main.java.srcDirs
        archiveClassifier.set("sources")
    }
    
     publishing {
         publications {
             bar(MavenPublication) {
                 groupId 'com.your'
                 artifactId 'artifact'
                 version '1.0'
                 artifact sourceJar
                 artifact("$buildDir/outputs/aar/YourLibraryName-release.aar")
                 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
                         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) {
                             final exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion')
                             exclusionNode.appendNode('groupId', '*')
                             exclusionNode.appendNode('artifactId', '*')
                         } else if (!dep.properties.excludeRules.empty) {
                             final exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion')
                             dep.properties.excludeRules.each { ExcludeRule rule ->
                                 exclusionNode.appendNode('groupId', rule.group ?: '*')
                                 exclusionNode.appendNode('artifactId', rule.module ?: '*')
                             }
                         }
                     }
                     configurations.compile.getDependencies().each { dep -> addDependency(dep, "compile") }
                     configurations.api.getDependencies().each { dep -> addDependency(dep, "compile") }
                     configurations.implementation.getDependencies().each { dep -> addDependency(dep, "runtime") }
                 }
             }
         }
    
         repositories {
             maven {
                 name = "GitHubPackages"
                 url = uri("https://maven.pkg.github.com/GITHUB_USERID/REPOSITORY")
                 credentials {
                     username = "GITHUB_USERID" // Better if you use env variable
                     password = "WRITE_ACCESS_TOKEN"
                 }
             }
         }
     }
    
  3. 在 Gradle 工具栏中,您可以在库模块的 publishing 子目录下找到 publish目录。 (确保在发布之前从模块目录中的 build->build 构建 aar)

  4. 要在您的项目中使用它,
    将以下内容添加到您的 应用级别 gradle 文件 内 android { }标签

     repositories {
         maven {
             name = "GitHubPackages"
             url = uri("https://maven.pkg.github.com/GITHUB_USERID/REPOSITORY")
             credentials {
                 username = "GITHUB_USERID" // Better if you use env variable 
                 password = "READ_ACCESS_TOKEN"
             }
         }
     }
    
  5. 终于在依赖中

     implementation 'com.your:artifact:1.0'
    

*Gitlab 也和这个很相似