I developed an app that uses a swagger-generated Java client. The client is in a project called "api", while the app is in a project called "app".
When I build the app via Build/Make Project
everything works fine. Also, when I try to run the app on an emulated or physical device Run/Run 'app'
. Running with debugger works as well. Even when I build the project via Buid/Generate Signed Bundle/APK
and choose the debug option, it works.
Now. The build fails when I try to create a signed release build. The following messages show:
Caused by: java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives: [...]\app\build\intermediates\transforms\dexBuilder\release\54, [...]\app\build\intermediates\transforms\externalLibsDexMerger\release\0, [...]\app\build\intermediates\transforms\dexBuilder\release\52.jar, [...]\app\build\intermediates\transforms\dexBuilder\release\53.jar
Caused by: com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives: [...]\app\build\intermediates\transforms\dexBuilder\release\54, [...]\app\build\intermediates\transforms\externalLibsDexMerger\release\0, [...]\app\build\intermediates\transforms\dexBuilder\release\52.jar, [...]\app\build\intermediates\transforms\dexBuilder\release\53.jar
Caused by: com.android.tools.r8.CompilationFailedException: Compilation failed to complete
Caused by: com.android.tools.r8.utils.AbortException: Error: Program type already present: io.swagger.client.ApiCallback
I am pretty new to developing with Android Studio and Gradle. I tried some solutions on Stack Overflow that already suggested to add some libraries but so far none of them worked with my problem.
To me, its especially suspicious that the last error message points to io.swagger.client.ApiCallback
.
Could it have anything to do with the fact that both settings.gradle
for "api" and "app" have the same content? Both look like this:
rootProject.name = "swagger-java-client"
. This is the only line in the file, but as far as I know, the settings.gradle
for "app" already hat that content. I cant remember changing it so its odd to me that it shows "swagger-java-client". Is that normal?
ProGuard has been mentioned in the comments to might be a problem. This is the only occurrence I could find, referencing it. In my build.gradle
for the "app"-project, there is this part:
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
However, removing it did not change anything.
When searching for "io.swagger" AS only finds this:
When searhing for "io.swagger.client.ApiCallback" AS only finds this:
As suggested, I tried adding
android {
defaultConfig {
multiDexEnabled true
}
}
and adding android.enableD8 = false
but that didn't help either.
Here are my build.gradle-Files (the first ones that caused the problems, without the suggested corrections I've tried so far.)
build.gradle (Module: api) (generated by Swagger):
apply plugin: 'idea'
apply plugin: 'eclipse'
group = 'io.swagger'
version = '1.0.0'
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.0'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
}
}
repositories {
jcenter()
}
if(hasProperty('target') && target == 'android') {
apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
android {
compileSdkVersion 25
buildToolsVersion '25.0.2'
defaultConfig {
minSdkVersion 14
targetSdkVersion 25
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
// Rename the aar correctly
libraryVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.aar')) {
def fileName = "${project.name}-${variant.baseName}-${version}.aar"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
dependencies {
provided 'javax.annotation:jsr250-api:1.0'
}
}
afterEvaluate {
android.libraryVariants.all { variant ->
def task = project.tasks.create "jar${variant.name.capitalize()}", Jar
task.description = "Create jar artifact for ${variant.name}"
task.dependsOn variant.javaCompile
task.from variant.javaCompile.destinationDir
task.destinationDir = project.file("${project.buildDir}/outputs/jar")
task.archiveName = "${project.name}-${variant.baseName}-${version}.jar"
artifacts.add('archives', task);
}
}
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
artifacts {
archives sourcesJar
}
} else {
apply plugin: 'java'
apply plugin: 'maven'
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
install {
repositories.mavenInstaller {
pom.artifactId = 'swagger-java-client'
}
}
task execute(type:JavaExec) {
main = System.getProperty('mainClass')
classpath = sourceSets.main.runtimeClasspath
}
}
dependencies {
compile 'io.swagger:swagger-annotations:1.5.21'
compile 'com.squareup.okhttp:okhttp:2.7.5'
compile 'com.squareup.okhttp:logging-interceptor:2.7.5'
compile 'com.google.code.gson:gson:2.8.2'
compile 'org.threeten:threetenbp:1.3.5'
testCompile 'junit:junit:4.12'
}
build.gradle (Module: app):
buildscript {
repositories {
maven { url 'https://maven.fabric.io/public' }
}
dependencies {
classpath 'io.fabric.tools:gradle:1.+'
}
}
apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
repositories {
maven { url 'https://maven.fabric.io/public' }
}
android {
compileSdkVersion 28
defaultConfig {
applicationId "projectName"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "0.1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:design:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation fileTree(dir: '../api/build/libs', include: ['*.jar'])
implementation 'com.google.code.gson:gson:2.8.2'
implementation 'com.squareup.okhttp:okhttp:2.7.5'
api project(path: ':api')
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8'
implementation 'org.jetbrains:annotations-java5:15.0'
}
I've found the answer to my question:
What I did was to create the swagger client, move the created folder into my project, adjust the settings.gradle
file and the build.gradle
file accordingly.
What went wrong
The way I configured things, Gradle would build the ´api´ project and then the app
project.
For some reason, when I configured things as release
, the app
project referenced the code of the api
project, as well as the resulting .jar
file. That's where the Program type already present
came from.
How I fixed it
api
project from the app
projectapi
separately.jar
file and put it in a folder inside your app
folder called libs
build.gradle (Module: app)
: implementation fileTree(dir: 'libs', include: ['*.jar'])
This included the finished .jar
file in the app
project and now I can build release. This should be done regardless, because the swagger client shouldn't be edited anyway, as it is generated code. So even just to not always build that part of the project, this should be done.
However, I still don't fully understand why there is this difference between the release
and the debug
build. I am still seeking insight to that.
EDIT: ADDITIONAL INFORMATION
I just found out: if, for some reason, I would have wanted to keep the Module: api
in my project I could have done so by removing the two dependencies in my Module: app
that start with implementation fileTree
.
The api-project is already included by the dependency api project(path: ':api')
. The filetree-lines were the ones causing the problem here, since they were redundant.