I have a problem. I have a structure of project looking like this:
P
|-SubP1
|-SubP2
There are two stub classes android.media.IRemoteDisplay in both packages. They differ in implementation, but that's not the matter - they will be replaced by system classes when I'll run the app on Android.
However, there is a problem - I can't build the project because dexMerger fails - it says there are two conflicting classes. I can understand that error - after all, there are really two conflicting classes :)
But when I try to exclude these files in build.gradle like this:
sourceSets {
main {
java {
srcDir 'src'
exclude '**/android/media/**'
}
}
}
The compilation fails because it can't find android.media.IRemoteControlDisplay class(and it's nested classes).
How can I still use these classes, but exclude them from resulting DEX file?
Please DON'T question if it's right to exclude the class from compiled project - it's the right thing to do, I already did it, but manually - by pre-compiling SubP1 and SubP2 to jars and then manually removing IRemoteController.class from these jar files and then including those jars in P.
I'll also be satisfied with that solution:
1. Build SubP1
2. Remove IRemoteControlDisplay.class from SubP1.jar
3. Build SubP2
4. Remove IRemoteControlDisplay.class from SubP2.jar
5. Add SubP1.jar and SubP2.jar as dependencies to P
6. Build P
If that's possible, please let me know.
Finally, I've been able to do it.
What I needed was a runtime dependency instead of compilation-time dependency. So, I've created submodule for SubP1 called SubSubP1(for example) moved the android.media.IRemoteDisplay into the SubSubP1, and created submodule SubSubP2 for SubP2, and did the same with stub class. Then I've declared the dependency as following for SubP1 and SubP2:
dependencies {
provided project(':SubP1:SubSubP1')
}
and
dependencies {
provided project(':SubP2:SubSubP2')
}
And that did the trick! Instead of compiling the classes, Gradle assumed that they will be loaded at runtime.
Related
With Java, we are excluding like this:
java {
srcDir 'src'
exclude '**/myTests/**'
}
I want to make the same thing with Kotlin.
I am trying to find some documentation on this in official documentation configuring Kotlin, but without any success.
What I've expected and already tried (and of course without any success):
kotlin {
srcDir 'src'
exclude '**/myTests/*.kt'
}
java {
srcDir 'src'
exclude '**/myTests/*.kt'
}
There isn't any Kotlin related configuration.
Why I am saying this: I have all the Kotlin files into the kotlin directory and Java files into java directory. But while configuring, I have added:
sourceSets {
main.java.srcDirs += "src/main/kotlin"
}
This means that with src/main/java, add source files from src/main/kotlin also while compiling.
This should solve your issue.
Android (likely need improvement, seems flaky):
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile.class).configureEach {
it.exclude('**/TestExcludeKotlinClass.kt')
}
If you use a Kotlin Gradle project, try this:
tasks.withType<KotlinCompile> {
exclude("**/packageToExlude/**")}
In my case, a non-Android project.
I came across a way to make this work specifically for Android unit tests (but I'm assuming it's adaptable) using a combination of other solutions here:
def filesToExclude = [
'**/*TestOne*.kt',
'**/*TestTwo*.kt',
...
]
tasks.withType(org.gradle.api.tasks.SourceTask.class).configureEach {
it.exclude(filesToExclude)
}
android.sourceSets.test.kotlin.exclude(filesToExclude)
In my particular case, the extra wildcards around the test name were needed due to other generation occurring (specifically, Dagger with kapt).
This seems to be a bit hacky way to approach it, but it works by ensuring the test target is excluded from all tasks that it could actually be excluded from (including both build & kapt tasks). The sourceSets exclusion is still necessary for the file not to be picked up for compilation (I think this is the Kotlin Gradle Plugin doing it, but it might also be the Android Gradle Plugin).
With Java, we are excluding like this:
java {
srcDir 'src'
exclude '**/myTests/**'
}
I want to make the same thing with Kotlin.
I am trying to find some documentation on this in official documentation configuring Kotlin, but without any success.
What I've expected and already tried (and of course without any success):
kotlin {
srcDir 'src'
exclude '**/myTests/*.kt'
}
java {
srcDir 'src'
exclude '**/myTests/*.kt'
}
There isn't any Kotlin related configuration.
Why I am saying this: I have all the Kotlin files into the kotlin directory and Java files into java directory. But while configuring, I have added:
sourceSets {
main.java.srcDirs += "src/main/kotlin"
}
This means that with src/main/java, add source files from src/main/kotlin also while compiling.
This should solve your issue.
Android (likely need improvement, seems flaky):
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile.class).configureEach {
it.exclude('**/TestExcludeKotlinClass.kt')
}
If you use a Kotlin Gradle project, try this:
tasks.withType<KotlinCompile> {
exclude("**/packageToExlude/**")}
In my case, a non-Android project.
I came across a way to make this work specifically for Android unit tests (but I'm assuming it's adaptable) using a combination of other solutions here:
def filesToExclude = [
'**/*TestOne*.kt',
'**/*TestTwo*.kt',
...
]
tasks.withType(org.gradle.api.tasks.SourceTask.class).configureEach {
it.exclude(filesToExclude)
}
android.sourceSets.test.kotlin.exclude(filesToExclude)
In my particular case, the extra wildcards around the test name were needed due to other generation occurring (specifically, Dagger with kapt).
This seems to be a bit hacky way to approach it, but it works by ensuring the test target is excluded from all tasks that it could actually be excluded from (including both build & kapt tasks). The sourceSets exclusion is still necessary for the file not to be picked up for compilation (I think this is the Kotlin Gradle Plugin doing it, but it might also be the Android Gradle Plugin).
Situation is that I in my project use library A. I also import external library which also has itself a library A. So as you can assume, when I try to compile, I receive Multiple DEX files define error which means that there are duplications.
However, If I remove my library from the project, I cannot use its provided methods. And I cannot find how can I remove that library from the module.
Any suggestions?
You should be able to exclude it like this:
compile('library:1.0.0') {
exclude group: 'something', module: 'something'
}
So do this on the external library for all the things you're using that's causing a problem.
From here: https://discuss.gradle.org/t/how-to-exclude-transitive-dependency/2119/2
define multiDexEnable True in your build.gradle(app)
defaultConfig {
multiDexEnabled true
}
and also define in dependency in same build.gradle(app)
compile 'com.android.support:multidex:1.0.1'
Thank you guys for your suggestions.
I didn't want to do as Michael suggested because I believe it is kind of useless (having multiple libraries with same purpose) (already knew this trick).
I have tried Ядм Жцмдшдт answer, but couldn't succeed in compiling code completely. I have received various errors.
In the end I have solved my own issue. What I did:
Remove library from my main app libs folder. Remove dependancies if any in Android Studio (File -> Project Structure -> Dependencies (On module app) -> remove if any regarding your library.
Clean project in Android Studio (Build -> Clean Project).
Go to the module where my library A is. Go to that module build.gradle file and add following line in the dependencies cluster
compile files('libs/libraryA.jar')
Sync code and enjoy results.
TLDR
I didn't have libraryA compiled in my external module but it threw me duplication error, that's where I was confused. By removing it from my main project and adding it to my module's compilations list solved the problem.
When I try to run my android application on an Android device, the gradle console reports the following error:
Error:Execution failed for task ':app:transformClassesWithJarMergingForDebug'.
> com.android.build.api.transform.TransformException: java.util.zip.ZipException: duplicate entry: com/loopj/android/http/AsyncHttpClient$1.class
When I search for the "AsyncHttpClient" class, I see that it's indeed being found in two separate locations:
/Users/Afflatus/.gradle/caches/modules-2/files-2.1/com.loopj.android/android-async-http/1.4.9/5d171c3cd5343e5997f974561abed21442273fd1/android-async-http-1.4.9-sources.jar!/com/loopj/android/http/AsyncHttpClient.java
/Users/Afflatus/.ideaLibSources/android-async-http-1.4.9-sources.jar!/com/loopj/android/http/AsyncHttpClient.java
The first path seems to suggest it's a "cache" file... so I've tried invalidating & restarting my cache, but both files are still there after the gradle gets rebuilt and I try to run the application. I've read in alternate posts that it can be resolved by deleting one of the files... So I went to the cache location and deleted all the files found in the "1.4.9" folder... unfortunantly after reopening Android Studio, a new cache file gets created and I get the same error.
Other posts (here, here,here, and here) suggest if I add "./gradlew clean" to the root directory it would rebuild the gradle again just for the run (as far as I understand). So I tried doing that as well:
Which made my app's folder look like this:
But unfortunantly, that didn't help things I still get the same error. What am I doing wrong? What should I be doing?
I added this line to my gradle.properties file and my app worked
android.enableJetifier=true
Sometimes duplicate classes exception means that one of your dependencies uses implicitly the older or newer (with +) version of some library you also use in your project,
To resolve this issue you may add such block of code (put your library version after 'force') to your build.gradle file (Module:app):
configurations {
all {
resolutionStrategy {
// do not upgrade above 3.12.0 to support API < 21 while server uses
// COMPATIBLE_TLS, or okhttp3 is used in project
force 'com.squareup.okhttp3:okhttp:3.12.0'
force 'com.squareup.okhttp3:logging-interceptor:3.12.0'
}
}
}
You may also exclude some group from your dependencies.
For a single dependency you way write:
dependencies {
// example
implementation('log4j:log4j:1.2.15') {
exclude group: 'javax.jms', module: 'jms'
}
}
Tested to work on Android Studio with Gradle Plugin version 3.4.2 and Gradle version 5.4.1.
Credits go to Dimitar Dimitrov and Schalk Cronjé from gradle org discussion group
That's because you have added some library two times in libs folder, this could happen sometimes when you have multiple versions of the same library in the libs folder. Check it and remove any duplicate jar files.
And the second option could be you have also added the dependency in gradle.build and also have a jar in libs folder.
So check both places and remove duplicate entries and then clean and build APK again.
Delete files with duplicate jar extensions in the libs folder. However, if there are no duplicate files and there is still a "Duplicate classes" error, look for the name in the rest of the "Duplicate classes ...." clause in the error section. For example, "duplicated classes 'dagger' bla bla". Delete the file named 'dagger' from the libs folder. (Be careful not to delete it with shift.)
In my case, I am using sensorocloud.jar and the compile 'com.loopj.android:android-async-http:1.4.9' in my gradle which caused the same error as yours. Because sensoro cloud SDK used loopj's async-http.
I managed to solve it by manually removing the duplicate .class files in the jar file. (i.e.
changing the extension from jar to zip
extract it
remove the com.loopj.android .class files)
(P.S. I have tried to search through the web to see if I could exclude certain class of a jar in gradle, but not succeed, e.g. I referenced this SO post)
This error can be caused by several things;
misconfigured package name
Activity views that is not well binded. - simply go to your launcher activity view and ensure context is defined well e.g "com.yourdomain.package"
Re-create your BuildConfig and set it up well.
Check if your project build.gradle. There it might be some maven duplicate dependency
Here's another situation that can cause duplicate class during the mergeDexClasses task. This can happen with later versions of android gradle.
If your build.gradle.kts script has a dependency in the form:
implementation(project(":mylib", configuration="default"))
that can cause duplicate classes. The correction is simple. Just change it to:
implemenation(project(:mylib"))
Here's the Android Studio's Team explanation:
Having both project(":lib") and project(path: ":lib", configuration: "default") in the runtime classpath means that AGP gets both build/classes/java/main and build/libs/lib.jar (run ./gradlew :lib:outgoingVariants --all to verify). Because paths differ, we'll get 2 dexing transforms happening: 1 incremental that produces dex per class under build/.transforms (the one processing dir) and another one which produces single dex (the one processing jar). Later on during merging this causes failure.
AGP never publishes to the default configuration, in fact java-library plugin does it only so it does not break older build scripts. Having an explicit configuration name used in the dependency declaration is discouraged and Gradle attributes should be used instead.
In an older version of AGP, I ran into a problem where adding the configuration value "default" fixed some issue I was having. Well that no longer works, and adding the "default" configuration you can get duplicate classes.
I'm currently discovering Android Studio and Gradle and migrating all the build chain of my project from bash scripts to Gradle configurations. It's probably going to be awesome in the end, but meanwhile I'm struggling.
What I want to do now is quite simple. I have a standard rule to generate Javadoc from my source (I took this snippet from http://snowdream.github.io/blog/android/2013/11/01/how-to-generate-javadocs-with-android-gradle-plugin/):
android.libraryVariants.all { variant ->
task("generate${variant.name.capitalize()}Javadoc", type: Javadoc) {
source = variant.javaCompile.source
def androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"
classpath = files(variant.javaCompile.classpath.files, androidJar)
options {
links "http://docs.oracle.com/javase/7/docs/api/"
linksOffline "http://d.android.com/reference","${android.sdkDirectory}/docs/reference"
}
exclude '**/BuildConfig.java'
exclude '**/R.java'
}
}
But my project also contains AIDL files and I don't want these aidl files (nor the .java files generated from them) to be included to the Javadoc.
I tried the rule:
exclude "**/$buildDir/**"
... and I tried a thousand others, but none works and my interfaces and Stub are processed into HTML files.
I beg for your help! Thanks a lot.
After hours of research about how Gradle and Groovy work, the best way I found is:
exclude {
it.file.path.contains('aidl')
}
However, I'm still unsatisfied. I do feel something less brutal could be done using variant.aidlCompile.sourceOutputDir that points to the Java files generated by AIDL. But I could not compare the iterated elements in the Closure to this File, or FileTree, or whatever shape I give it...
EDIT:
After further research, another method is to hide the interfaces defined in AIDL using a custom Doclet that supports #hide tag (like Doclava). I preferred this method because it doesn't cause errors during Javadoc generation.