How to make an equivalent of 'compileOnly' for AAR artifacts?
Asked Answered
H

1

4

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

Howard answered 2/4, 2020 at 18:0 Comment(13)
"For some reason i'm working with two different apps" -- IMHO, you need to explain this in much more detail. What are these apps? What is their relationship? Why are you in some situation where both are being compiled together, such that what each app has in its dependencies somehow affects the other app?Zabaglione
@Zabaglione i've update a question. just check last sectionHoward
Isn't this then a matter of "testApp" using api instead of implementation for com.google.android.material:material:1.0.0?Zabaglione
@Zabaglione it doesn't matter when use api or implementation for me, since both of them add sources to runtime classpath. So i will get a same error.Howard
@Howard did you try excluding like exclude group: 'com.google', module: 'your_module'Prostration
@SureshMaidaragi yes, i've tried that aswell. If you exclude a module, you need to provide a runtime source to assemble apk. It will work if my two project have a same gradle rootm but as i said, i can't do this.Howard
By "final dex file" you mean the merged app on the device? Do you mean that the library is included but unused methods/classes are stripped? If that's the case you might get lucky by modifying the proguard settings of the original apk to prevent stripping of any code from that library.Diocletian
If that's not the case, can you clarify what exactly is included in the final package on the device?Diocletian
Where is the relation between aar , com.app and com.app.test ... I don't see what's your problem exactly..Stallworth
@IbrahimAli the relation is described quite clearly in topic. "com.google.android.material:material:1.0.0" - aar. "com.app" and "com.app.test" my apps which has own gradle root and use this material library. Since they are merged via adb instrumentation, i have a problem with using aar dependencies in "com.app.test" if they are also have been used in "com.app".Howard
@Howard The .aar file by default won't depend on their dependencies untils they make their com.material transitive.. AND com.app and com.app.test should be separated with separated manifests .. What I missing here ?Stallworth
@IbrahimAli you're missing that after adb instrumentation, they will have one runtime classpath. The test app and main app code will be executed on one proccess. If they both have at least one similar dependency , runtime classpath will contains two instance of similar binaries. For this situation i can use compileOnly but it will works only on JAR sources.Howard
I don't really see why your compileOnlyAar 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
H
-2

On the Right side of android studios goto gradle>your libraryapp>Run configurations>asssemble .AAR will be present in outputs folder of your selected project

Hansel answered 15/4, 2020 at 12:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.