【发布时间】:2020-04-02 18:00:35
【问题描述】:
在现实世界中,如果您正在为 android 项目编写 ui 测试,在 Android Studio 中按“运行”后,它将组装两个应用程序:“your.cool.app”和“your.cool.app.test” .测试应用程序将在 android manifest 中有类似:
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="your.cool.app"/>
这实际上意味着,如果您同时安装它们,您的测试应用程序将与真正的应用程序合并 - 所有依赖项(第三方库等)都将位于一个运行时类路径中。因此,如果两个应用程序将具有某种类型的库,则它们无法合并,因为在运行时类路径中不可能有多个类,否则您将获得 duplicated classes 异常。
这就是为什么您拥有 Android 插件的功能,它允许您为测试应用添加依赖项 - androidTestImplementation。因此,如果您添加两个类似的依赖项,例如:
implementation 'com.google.android.material:material:1.0.0'
androidTestImplementation 'com.google.android.material:material:1.0.0'
Gradle 将解决此类问题。因此,对于真正的应用程序,它将添加到 compile 和 runtime 类路径中,而对于测试应用程序,它只会为 compile 类路径添加(这就是您可以导入的原因测试类中的所有依赖项)
出于某种原因,我正在使用两个不同的应用程序。当我为这两个应用程序添加依赖项时,例如:
implementation 'com.google.android.material:material:1.0.0'
我会有一个Duplicate classes '...'
是的,最好的解决方案是制作一个单声道存储库,并让 Android Gradle 插件解决这个问题... 但我不能,因为在这种情况下你不能分享你的测试代码,没有主代码。
是的,我们已经有了一个“compileOnly”,它可以完美运行(它只是添加一个依赖项来编译类路径),但它只适用于 JAR。所以我需要一个像'compileOnly'这样的工件配置,但对于AARs
我已经尝试过的:
allprojects {
createCompileOnlyAarConfiguration(project)
}
def createCompileOnlyAarConfiguration(Project project) {
def compileOnlyAarConf = project.configurations.create('compileOnlyAar')
compileOnlyAarConf.visible = false
compileOnlyAarConf.transitive = false
project.gradle.addListener(new DependencyResolutionListener() {
@Override
void beforeResolve(ResolvableDependencies resolvableDependencies) {
compileOnlyAarConf.dependencies.each { dependency ->
project.dependencies.add('compileOnly', dependency)
}
project.gradle.removeListener(this)
}
@Override
void afterResolve(ResolvableDependencies resolvableDependencies) {
}
})
}
所以现在我可以做类似的东西:
dependencies {
compileOnlyAar 'com.google.android.material:material:1.0.0'
}
通过这种配置,我可以使用这些依赖项中的所有类,但在最终的 dex 文件中,我使用过的方法没有参考。这就是为什么我得到java.lang.NoSuchMethodError: java.lang.NoSuchMethodError: No static method
我还发现了一个gradle plugin,它实际上添加了一个'compileOnlyAar'配置,但它只适用于java模块
我在 gradle 环境中不是那么好,所以如果有人能指出我如何为 AAR 工件制作 'compileOnly' 会很酷。
更新:
“出于某种原因,我正在使用两个不同的应用程序” - 意味着我也有 'your.cool.app' 和 'your.cool.app.test' 应用程序。但是他们有自己的 gradle 依赖项,并且测试应用程序在 AndroidManifest 中有 instrumentation 属性。 'your.cool.app' - 主代码库,'your.cool.app.test' - 所有 ui 测试。安装两者后,我只需通过adb shell am instrument 运行测试。测试应用和主应用需要依赖com.google.android.material:material:1.0.0,所以它们都包含:
dependencies {
implementation 'com.google.android.material:material:1.0.0'
}
正如我所说,如果我安装这两个应用程序并通过adb shell am instrument 开始测试,我将得到重复类异常,因为两个二进制文件将在运行时类路径中出现两次。
为什么我需要这个结构?我只能共享没有主代码库的测试应用程序。所以我没有办法制作标准的monorepo
【问题讨论】:
-
“出于某种原因,我正在使用两个不同的应用程序”——恕我直言,您需要更详细地解释这一点。这些应用程序是什么?他们是什么关系?为什么在某些情况下两者都被编译在一起,以至于每个应用程序在其依赖项中的内容会以某种方式影响另一个应用程序?
-
@CommonsWare 我更新了一个问题。只需检查最后一部分
-
这难道不是“testApp”使用
api而不是implementationforcom.google.android.material:material:1.0.0的问题吗? -
@CommonsWare 对我来说使用 api 或实现并不重要,因为它们都将源添加到运行时类路径。所以我会得到同样的错误。
-
@HeyAlex 您是否尝试排除类似排除组:'com.google',模块:'your_module'
标签: android gradle android-gradle-plugin build.gradle gradle-plugin