Android X + Truth + Guava 测试编译问题

标签 android gradle guava androidx google-truth

我有一个 Android 库(称为 api)gradle 模块作为一个更大项目的一部分。我刚刚将整个项目迁移到 AndroidX。我现在在 api lib 上运行仪器测试时出现此错误:

 Task :api:checkDebugAndroidTestDuplicateClasses FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':api:checkDebugAndroidTestDuplicateClasses'.
> 1 exception was raised by workers:
  java.lang.RuntimeException: java.lang.RuntimeException: Duplicate class com.google.common.util.concurrent.ListenableFuture found in modules jetified-guava-25.1-android.jar (com.google.guava:guava:25.1-android) and listenablefuture-1.0.jar (com.google.guava:listenablefuture:1.0)

如果我检查 debugAndroidTest 变体的运行时类路径:

./gradlew api:dependencies --configuration debugAndroidTestRuntimeClasspath | grep --color -E "guava|$"

我得到了这个输出。我可以看到问题:

------------------------------------------------------------
Project :api
------------------------------------------------------------
debugAndroidTestRuntimeClasspath - Resolved configuration for runtime for variant: debugAndroidTest
+--- project :test_utils
|    +--- project :core
...
|    +--- project :api (*)
|    +--- com.google.android.material:material:1.1.0-alpha03
|    |    +--- androidx.annotation:annotation:1.0.1 -> 1.1.0-alpha01
|    |    +--- androidx.appcompat:appcompat:1.1.0-alpha01
|    |    |    +--- androidx.annotation:annotation:1.0.0 -> 1.1.0-alpha01
|    |    |    +--- androidx.core:core:1.1.0-alpha01 -> 1.1.0-alpha03
|    |    |    |    +--- com.google.guava:listenablefuture:1.0 // <------ GUAVA
|    |    |    |    +--- androidx.annotation:annotation:1.0.1 -> 1.1.0-alpha01
...
+--- com.google.truth:truth:0.42
|    +--- com.google.guava:guava:25.1-android / <------ MORE GUAVA
|    |    +--- com.google.code.findbugs:jsr305:3.0.2
|    |    +--- com.google.errorprone:error_prone_annotations:2.1.3 -> 2.3.1
|    |    +--- com.google.j2objc:j2objc-annotations:1.1
|    |    \--- org.codehaus.mojo:animal-sniffer-annotations:1.14
|    +--- org.checkerframework:checker-compat-qual:2.5.3
|    +--- org.checkerframework:checker-qual:2.5.3
|    +--- junit:junit:4.12 (*)
|    +--- com.googlecode.java-diff-utils:diffutils:1.3.0
|    +--- com.google.auto.value:auto-value-annotations:1.6.2
|    \--- com.google.errorprone:error_prone_annotations:2.3.1
...

AndroidX 核心依赖于 guava 的新“ListableFuture-only”构建,而 Truth 依赖于完整的 Guava 25。

我想我了解 ListenableFuture 的潜在问题:https://groups.google.com/forum/#!topic/guava-announce/Km82fZG68Sw

什么是正确的解决方案?

我不想将 Guava 完全排除在 Truth 之外(否则 Truth 无法编译)

androidTestImplementation("com.google.truth:truth:0.42") {
    exclude group: 'com.google.guava', module: 'guava'
}

我可以排除并强制更新 Guava 27 使其成为一级依赖吗:

androidTestImplementation("com.google.truth:truth:$rootProject.ext.truthVersion") {
    exclude group: 'com.google.guava', module: 'guava'
}
// must add guava as top level dependency to force Truth to use latest version
androidTestImplementation 'com.google.guava:guava:27.0.1-android'

如果我这样做,我应该使用 guava 的 android 还是 JRE 版本?

附带问题:

为什么在查看 compile 类路径时看不到 guava 依赖项?该错误是编译时错误,不是运行时错误

./gradlew api:dependencies --configuration debugAndroidTestCompileClasspath | grep --color -E "guava|$"

结果部门:

debugAndroidTestCompileClasspath - Resolved configuration for compilation for variant: debugAndroidTest
+--- project :test_utils // <----------- why are test_utils deps not listed here???
...
+--- com.google.truth:truth:0.42
|    +--- com.google.guava:guava:25.1-android <------ GUAVA
|    |    +--- com.google.code.findbugs:jsr305:3.0.2
|    |    +--- org.checkerframework:checker-compat-qual:2.0.0 -> 2.5.3
|    |    +--- com.google.errorprone:error_prone_annotations:2.1.3 -> 2.3.1
|    |    +--- com.google.j2objc:j2objc-annotations:1.1
|    |    \--- org.codehaus.mojo:animal-sniffer-annotations:1.14
|    +--- org.checkerframework:checker-compat-qual:2.5.3
|    +--- org.checkerframework:checker-qual:2.5.3
|    +--- junit:junit:4.12 (*)
|    +--- com.googlecode.java-diff-utils:diffutils:1.3.0
|    +--- com.google.auto.value:auto-value-annotations:1.6.2
|    \--- com.google.errorprone:error_prone_annotations:2.3.1
...
+--- com.google.truth:truth:{strictly 0.42} -> 0.42 (c)
+--- com.google.guava:guava:{strictly 25.1-android} -> 25.1-android (c) // <--------- why is this listed again here at top level?

更新:

更新到 Truth 0.43 后,我现在看到这个错误:

> Could not resolve all artifacts for configuration ':mymodule:debugAndroidTestRuntimeClasspath'.
   > Could not resolve com.google.guava:listenablefuture:{strictly 1.0}.
     Required by:
         project :mymodule
      > Cannot find a version of 'com.google.guava:listenablefuture' that satisfies the version constraints:
           Dependency path 'example:mymodule:unspecified' --> 'com.google.truth:truth:0.43' --> 'com.google.guava:guava:27.0.1-android' --> 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
           Constraint path 'example:mymodule:unspecified' --> 'com.google.guava:listenablefuture:{strictly 1.0}' because of the following reason: debugRuntimeClasspath uses version 1.0
           Dependency path 'example:mymodule:unspecified' --> 'example:myothermodule:unspecified' --> 'andrexampleoidx.core:core:1.1.0-alpha04' --> 'com.google.guava:listenablefuture:1.0'
           Constraint path 'example:mymodule:unspecified' --> 'com.google.guava:listenablefuture:{strictly 1.0}' because of the following reason: debugRuntimeClasspath uses version 1.0
            ...
           Dependency path 'example:mymodule:unspecified' --> 'example:myothermodule:unspecified' --> 'androidx.core:core:1.1.0-alpha04' --> 'androidx.concurrent:concurrent-futures:1.0.0-alpha02' --> 'com.google.guava:listenablefuture:1.0'
           Constraint path 'example:mymodule:unspecified' --> 'com.google.guava:listenablefuture:{strictly 1.0}' because of the following reason: debugRuntimeClasspath uses version 1.0
           ...

   > Could not resolve com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava.
     Required by:
         project :mymodule > com.google.truth:truth:0.43 > com.google.guava:guava:27.0.1-android
      > Cannot find a version of 'com.google.guava:listenablefuture' that satisfies the version constraints:
           Dependency path 'example:mymodule:unspecified' --> 'com.google.truth:truth:0.43' --> 'com.google.guava:guava:27.0.1-android' --> 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
           Constraint path 'example:mymodule:unspecified' --> 'com.google.guava:listenablefuture:{strictly 1.0}' because of the following reason: debugRuntimeClasspath uses version 1.0
           Dependency path 'example:mymodule:unspecified' --> 'example:myothermodule:unspecified' --> 'androidx.core:core:1.1.0-alpha04' --> 'com.google.guava:listenablefuture:1.0'
           ...
           Dependency path 'example:mymodule:unspecified' --> 'example:myothermodule:unspecified' --> 'androidx.core:core:1.1.0-alpha04' --> 'androidx.concurrent:concurrent-futures:1.0.0-alpha02' --> 'com.google.guava:listenablefuture:1.0'
           Constraint path 'example:mymodule:unspecified' --> 'com.google.guava:listenablefuture:{strictly 1.0}' because of the following reason: debugRuntimeClasspath uses version 1.0
           ...

   > Could not resolve com.google.guava:listenablefuture:1.0.
     Required by:
         project :mymodule > androidx.core:core:1.1.0-alpha04
         project :mymodule > androidx.concurrent:concurrent-futures:1.0.0-alpha02
      > Cannot find a version of 'com.google.guava:listenablefuture' that satisfies the version constraints:
           Dependency path 'example:mymodule:unspecified' --> 'com.google.truth:truth:0.43' --> 'com.google.guava:guava:27.0.1-android' --> 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
           Constraint path 'example:mymodule:unspecified' --> 'com.google.guava:listenablefuture:{strictly 1.0}' because of the following reason: debugRuntimeClasspath uses version 1.0
           ...
           Dependency path 'example:mymodule:unspecified' --> 'example:myothermodule:unspecified' --> 'androidx.core:core:1.1.0-alpha04' --> 'androidx.concurrent:concurrent-futures:1.0.0-alpha02' --> 'com.google.guava:listenablefuture:1.0'
           Constraint path 'example:mymodule:unspecified' --> 'com.google.guava:listenablefuture:{strictly 1.0}' because of the following reason: debugRuntimeClasspath uses version 1.0
           ...

如果我没理解错的话:

  • Truth 0.43 取决于 Guava 27
  • Guava 27 依赖于 9999.0-empty,因为它内部包含 ListenableFuture 的副本,而版本 9999.0-empty 是“空”的。
  • AndroidX 核心等其他库依赖于 listenablefuture:1.0
  • 解决冲突时(我应该使用版本 1.0 还是 9999.0?)Gradle 将选择最新版本,在本例中为 9999.0。
  • 因此 AndroidX 核心实际上通过 Guava 从类路径获取 ListenableFuture,但它并不明智。
  • 但是,strictly 关键字强制使用 1.0
  • 发生这种情况是因为 Android Gradle 插件强制运行时和编译时类路径依赖于同一版本 =( read more here

所以目前我不知道该怎么做。

最佳答案

要解决冲突,只需添加对 Guava 27 的依赖就足够了。这样做应该会自动(尽管 the post you linked 中描述的“版本 99 不存在”黑客攻击)阻止 ListenableFuture 的第二个副本被拉入。(如果没有,请告诉我们!)

由于您在 Android 库上工作,听起来您需要 guava-27.0.1-android 而不是 -jre

抱歉,我对你的附带问题一无所知。很抱歉这么长时间没有回复。

关于Android X + Truth + Guava 测试编译问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54635950/

相关文章:

java - 我尝试使用 Retrofit 将 JSON 解析为 Java

java - 使用特定大小时如何使单行EditText正确绘制自身

android - Gradle 依赖 : How to figure out what's causing this duplication

java - 使用Optional验证输入法参数

java - 如何获取嵌套 `ArrayListMultimap`中的值

java - 使用 robotium 在 ListView 中单击图像时可扩展 ListView 滚动

android - 如何更改 TestEngine ID 以运行 JUnit 5?

android - 因为来自 sdk 的 flutter_test 的每个版本都依赖于路径 1.8.1,而 easy_localization 3.0.0 依赖于路径 >=1.8.0-nullsafety

gradle - 使用 Gradle 的 Kotlin 脚本配置获取属性的正确方法

java - Collections2.filter 方法不满足传递的参数