Gradle provided JAR dependency not working as expected - android

I've been stuck on this problem all day, I'm hoping someone can help me get my program running.
I am building an Annotation Processor for Android, it currently has two modules: "mymodule" and "mymodule-compiler"
mymodule-compiler depends on mymodule:
dependencies {
compile project(':mymodule')
...
}
mymodule uses the netflix "provided" plugin for Android:
apply plugin: 'nebula.provided-base'
dependencies {
provided 'com.google.android:android:4.1.1.4'
}
This actually works perfectly, however when I run my compiler unit test suite, a lot of tests fail because the Android dependency specified is v4.1.1.4 and I actually need v6.0. v6.0 is not available on Maven (and looks like it never will be because Google haven't updated it since 2012).
I found out that the Android SDK provides a "Unit Test" JAR version of Android for each release, which is the entire SDK "stubbed out". I assume the versions on Maven are stubbed out too(?). I can easily grab that JAR and put it in my project under libs/.
Now when I update the mymodule gradle file:
dependencies {
provided files('libs/android.jar')
}
My project still compiles correctly and I can see v23 files! However, when I run my unit tests, I see the following error:
!!! JUnit version 3.8 or later expected:
java.lang.RuntimeException: Stub!
at junit.runner.BaseTestRunner.<init>(BaseTestRunner.java:5)
at junit.textui.TestRunner.<init>(TestRunner.java:54)
at junit.textui.TestRunner.<init>(TestRunner.java:48)
at junit.textui.TestRunner.<init>(TestRunner.java:41)
at com.intellij.rt.execution.junit.JUnitStarter.junitVersionChecks(JUnitStarter.java:205)
at com.intellij.rt.execution.junit.JUnitStarter.canWorkWithJUnitVersion(JUnitStarter.java:188)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:65)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
It shouldn't matter that the Android JAR is stubbed out because I never execute Android code, I only need it for imports.
Does anyone have any ideas about what I'm seeing and how I could fix this?
EDIT 1: More info:
If I remove the Android dependencies from mymodule entirely, and instead put the following into mymodule-compiler.gradle:
dependencies {
testCompile files('libs/android.jar')
...
}
All my tests will pass. This is obviously not a fix though, as 'mymodule' does need the Android dependency.
EDIT 2:
Doing the same test as EDIT 1, but using the netflix provided scope instead of testCompile works too. If I use the provided scope in both projects though, I get the same Stub! exception.

[Worked out together with OP]
The local jar contains additional classes over the maven jar which seem to interfere.
To get it working delete these from the jar (specifically in this case: all junit classes).
Comparison of the two jars (left is maven jar, right is local jar):

Related

java.lang.NoClassDefFoundError: Test Module could not resolve classes of other Feature Module, Unit Testing - Kotlin - Android

I have the following Project Structure:
Main App
--app
--featureModule1
--featureModule2
--TestKit(Library module)
where Testkit has all dependencies including app and featureModules. The Testkit included all the unit tests related to the features in app and featuremodules.
When i run unit tests from Android studio(Right click-> Run Test in Testkit), they run fine. However whenever i try running it from gradle command: ./gradlew TestKit:testInternalDebugUnitTest, it throws NoClassDefFoundError for all the dependencies of other modules(for both app and feature modules).
Also i have already added implementation and testImplementation dependencies of all the modules in test module.
Please suggest:
Do i need to add path of classes generated in other modules, if yes pls guide where.
Also running Test with Coverage fails with
java.lang.VerifyError: Bad return type
Please suggest what am i doing wrong here.
Note:
I have created a Testkit because of the multiple open issues of Roboelectric in feature modules for ex: https://github.com/robolectric/robolectric/issues/5428
Versions
AS: 4.0.1
gradle: 4.0.1
gradle-wrapper: 6.5
Robolectric: 4.3.1
I was able to solve the problem using the following runtime test dependencies:
testRuntimeOnly(files("${projectDir}/../app/build/intermediates/app_classes/internalDebug/classes.jar"))
testRuntimeOnly(files("${projectDir}/../featureModule1/build/intermediates/app_classes/internalDebug/classes.jar"))
testRuntimeOnly(files("${projectDir}/../featureModule2/build/intermediates/app_classes/internalDebug/classes.jar"))
inside build.gradle of Testkit.
Can refer to the following link for more details:https://github.com/android/app-bundle-samples/issues/11#issuecomment-675725610

How to get a compile error if a JAR dependency is missing?

I have an Android project with the following dependencies:
-- Android App
---> MySDK.Jar
------> 'org.apache.commons:commons-lang3:3.5'
This is MySDK.jar that has a dependency on commons-lang3.
I'm working on Android Studio and I'm thus using Gradle.
Here is my problem:
I have shared "MySDK.Jar" to someone and he has built his own Android App on top of it.
It works but we have seen that the compiler doesn't notice the missing dependency on 'org.apache.commons:commons-lang3:3.5'. At run-time there will be a crash if the code using 'org.apache.commons:commons-lang3:3.5' is called. One may not notice the problem if he doesn't call the code using this library.
I know that we can solve this issue by adding the following line to Android App build.gradle file:
compile 'org.apache.commons:commons-lang3:3.5'
I'm wondering if there is a way to get a compile error indicating such missing dependencies? It is indeed better to see the dependency problem at compilation time rather than at runtime.
What are the recommended good practices for this?
Thanks!
commons-lang3 is a transitive dependency of Android App. As such, it is often not needed for compilation - there are exceptions, especially regarding multiple levels of inheritance. So at compile time you (usually) do not know whether you miss a transitive dependency that you need at runtime.
This is where Gradle comes in. Gradle can (as Maven) resolve dependencies transitively from a Maven repository (as MavenCentral). If you put MySDK into a Maven repository (like Nexus or Artifactory, which have open source versions), everyone using MySDK will automatically draw commons-lang3 so you will not miss anything at runtime.
If you are just adding the jar file in your project you can't warning about the missing dependencies.
To do it you have to publish the jar file in a maven repo.
In this way you have a pom file which describes the dependencies that gradle has to download.
Provide a method like MySDK.init() int your MySDK.jar,call a method whe is belong to org.apache.commons:commons-lang3:3.5' in the MySDK.init() method, then put init() into onCreate() of your Application,
Another way is,putorg.apache.commons:commons-lang3:3.5 into MySDK.jar,
Hope it helps you :)

Android gradle JAR dependency ends up declared as native

In my Android projects build.gradle file I have:
dependencies {
compile 'org.apache.directory.studio:org.apache.commons.io:2.4'
compile 'org.apache.commons:commons-collections4:4.0'
}
When I run aapt dump badging I see:
native-code: 'commons-io-2.4.jar'
Clearly commons-io is not native code but the Android Gradle plugin thinks it is. How can I tell the Android Gradle plugin this library does not contain native code? Also curious how did it decide what architecture this library is? It seems to have picked some variety of arm since the APK won't install on a intel-based Android emulator, it gives the error: INSTALL_FAILED_CPU_ABI_INCOMPATIBLE.
You may be using the incorrect Commons IO library. Try:
http://mvnrepository.com/artifact/commons-io/commons-io/2.4
The version you're using seems to be specific to Apache Directory Studio 2.4 which seems to be a desktop app.
I once had troubles introducing a dependency on commons-io:2.4 into my build code. The problem is that Gradle itself comes with a version of commons-io. In my case it could be fixed with this:
sourceSets {
main {
compileClasspath = configurations.compile.minus files("$gradle.gradleHomeDir/lib/commons-io-1.4.jar")
}
}
I took this snippet from My Gradle project depends on commons-io 2.4, but Gradle puts $GRADLE_HOME/commons-io-1.4.jar into the classpath, causing failures
But be aware that it is not recommended to use different versions of libraries to come with Gradle.
You can find more information about this here: https://discuss.gradle.org/t/unable-to-use-commons-io-2-4-because-gradle-forces-the-loading-of-commons-io-1-4/8021
This might aswell be completely unrelated :)

Dependency issues in reference project

I have an issue that dependencies of a project that is referenced as a dependency module in my android app seem not to be included into the .apk file of my android application.
Project Setup
Android App (Android studio & gradle)
Java desktop application (IntelliJ/maven/gradle project)
Java model (classes & features used by both android & desktop app)
The "Java model" is added as a dependency to both Android App and Java Desktop application.
When I run the desktop application, the dependencies of the JavaModel are resolved via maven, incuded into the application and everything runs smoothly.
From an Android Studio point of view, I have imported the JavaModel as a module into the android project and gradle is used to resolve the dependencies. I have set up the following gradle files:
Android App "settings.gradle"
include ':app'
include ':JavaModel'
project(':JavaModel').projectDir=new File('../../JavaModel')
Android App "build.gradle"
dependencies {
compile project(':JavaModel')
// and more...
}
Java Model "build.gradle"
dependencies {
compile(
'org.apache.httpcomponents:httpclient:4.4.1'
// and more ...
)
}
Everything compiles just fine without any errors and a .apk can be created and runs on my test device. However, as soon as I access features within the app that are provided by the "Java model" (in this example, I am using the HttpClient class from the org.apache.httpcomponents:httpclient:4.4.1 dependency), I get the following exception:
java.lang.ClassNotFoundException: Didn't find class
"org.apache.http.impl.client.HttpClients"
Plese note that this is just an example case and the issue also occurs with all other dependencies that are only referenced in the "JavaModel", but not in the Android app itself.
It seems to me like the dependencies of the "JavaModel" work just fine at compile time, since everything executes just fine, but are then not included into the .apk file and therefore cause this exeption.
The question is how can I (correctly) make sure that even dependencies of a dependency project are included into the .apk file?
Apache http client conflicts with android one, if you want to use recent one, you need to use android port https://hc.apache.org/httpcomponents-client-4.3.x/android-port.html
Regarding "JavaModel". If dependencies of JavaModel are compile dependencies it all must work fine (assuming dependency does not have fancy code like classloaders)

Android Studio 1.1, simple junit test setup

I have read around, there are a number of extensive answers (like this one) but the Android world evolves so fast that they seem to be a bit outdated and the official documentation still refers to Eclipse with ADT.
I am running AS 1.1 and I am trying to setup simple junit tests to run on the emulator, without Robolectric. If I don't include junit in my build.gradle, it can't find #After, #Before and #Test and I get package org.junit does not exist. Upon adding
// unit tests
androidTestCompile 'junit:junit:4.11'
the error becomes
Error:duplicate files during packaging of APK
[...]/app/build/outputs/apk/app-debug-test-unaligned.apk
Path in archive: LICENSE.txt
Origin 1: [...]/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar
Origin 2: [...]/.gradle/caches/modules-2/files-2.1/junit/junit/4.11/4e031bb61df09069aeb2bffb4019e7a5034a4ee0/junit-4.11.jar
You can ignore those files in your build.gradle:
android {
packagingOptions {
exclude 'LICENSE.txt'
}
}
Following the console suggestion of excluding LICENSE.txt, it then works but it feels like a hack. So I'm wondering, am I maybe missing something? Thanks.
Android Studio unit testing support comes in 1.1 Beta 4 (release announcement) with Gradle plugin version 1.1.0-rc1.
More info in official document.
However it is experimental feature for now. E.g. it breaks installDebug gradle task.
For using JUnit in instrumentation tests there is good guide for Espresso library and another covering new AndroidJUnitRunner.
If it's any use I set up a boiler plate project allowing the use of Unit tests and Espresso tests by the use of switching build variants. You won't need the use of any third party plugins with this.
https://github.com/hitherejoe/Android-Boilerplate

Categories

Resources