In real world, if you're writing ui tests for android project, after press 'Run' from Android Studio, it will assemble two apps: 'your.cool.app' and 'your.cool.app.test'. Test app will have in android manifest something like:
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="your.cool.app"/>
Which actually will be mean, that if you install them both, your test app will be merged with a real one - all dependencies (third-party libs etc..) will be in one runtime classpath. So if two apps will have some sort of libraries, they can't be merged, since it's impossible to have more than one class in runtime classpath overwise you will get a duplicated classes exception.
That's why you have a functionality of Android Plugin which allows you to add dependencies for test app - androidTestImplementation
. So if you add two similar dependencies like:
implementation 'com.google.android.material:material:1.0.0'
androidTestImplementation 'com.google.android.material:material:1.0.0'
Gradle will resolve this kind of issue. So for real app it will add to compile and runtime classpaths and for test app it will add only for compile classpath (that's why you can import all stuff from dependency in test classes)
For some reason i'm working with two different apps. And when i'm adding a dependency for these two apps like:
implementation 'com.google.android.material:material:1.0.0'
I will have a Duplicate classes '...'
Yes, the best solution is to make a mono repo, and let Android Gradle Plugin resolve this... But i can't, since in this situation you can't share your test code, without main code.
Yes, we already have an 'compileOnly' which works perfectly (it's only add a dependency to compile classpath) but it only works for JARs. So i need an artifact configuration like 'compileOnly' but for AARs
What i've already tried:
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) {
}
})
}
So now i can make something like:
dependencies {
compileOnlyAar 'com.google.android.material:material:1.0.0'
}
And with this configuration i can use all classes from these dependency but in final dex file where is no reference for methods that i've used. That's why i get java.lang.NoSuchMethodError: java.lang.NoSuchMethodError: No static method
I also found a gradle plugin, which actually adds a 'compileOnlyAar' configuration, but it works only with java module
I'm not that good in gradle enviroment, so it would be cool if someone can point me how to make 'compileOnly' for AAR artifacts.
UPDATE:
"For some reason i'm working with two different apps" - means, that i have 'your.cool.app' and 'your.cool.app.test' apps aswell. But they have own gradle dependencies and test app have instrumentation
attribute in AndroidManifest. 'your.cool.app' - main codebase, 'your.cool.app.test' - all ui tests. After installing both i just run tests via adb shell am instrument
. Test app and main app need dependency com.google.android.material:material:1.0.0
so both of them contains:
dependencies {
implementation 'com.google.android.material:material:1.0.0'
}
So as i said, if i install both apps and start test via adb shell am instrument
, i will get duplicated class exception, since both binaries will be twice in runtime classpath.
Why i need this structure? I can share only test app without main codebase. So there is no way to me to make a standard monorepo
api
instead ofimplementation
forcom.google.android.material:material:1.0.0
? – Zabaglioneaar
,com.app
andcom.app.test
... I don't see what's your problem exactly.. – Stallworth.aar
file by default won't depend on their dependencies untils they make theircom.material
transitive.. ANDcom.app
andcom.app.test
should be separated with separated manifests .. What I missing here ? – StallworthcompileOnlyAar
solution isn't working. You are adding that to the test module right? In the end everything should be in the same classpath and use the same classloader so in theory it should work. Can you explain a bit more in detail how the setup with your workaround looks? – Diocletian