Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DexArchiveMergerException only on release

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?

Update 1

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.

Update 2

When searching for "io.swagger" AS only finds this:

only one match found

When searhing for "io.swagger.client.ApiCallback" AS only finds this:

only one match found

Update 3

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'
}
like image 958
Benjamin Basmaci Avatar asked Jan 07 '19 18:01

Benjamin Basmaci


4 Answers

Here are some workaround :

  • Can you please make sure you have multiDexEnabled set to true as shown below in your gradle settings:

    android {
        defaultConfig {
            multiDexEnabled true
        }
    }
    
  • Try android.enableD8 = false in grade.properties
like image 161
stefan Avatar answered Oct 06 '22 00:10

stefan


Caused by: com.android.tools.r8.utils.AbortException: Error: Program type already present: io.swagger.client.ApiCallback

Means there are multiple dependencies contain this class, maybe different versions, so you need to exclude them.

  1. Press shift twice, search io.swagger, then record the found jar files.

  2. If these jar files are simply duplicated, keep 1 in build.gradle.


  1. If these jar files are modules of other dependency, enter gradlew -q app:dependencies in the terminal, find the root dependency names.

  2. In build.gradle, choose 1 dependency to keep, for others add exclude command.

For example change

implementation 'xxx:xxx'

to

implementation ('xxx:xxx') { exclude module 'module-name-to-be-excluded' }

UPDATE

Some options to try:

Change build variants of both modules to release, then Build\Build APK

If success, copy the text from Gradle Console.

Next Build\Generate Signed APK...

Compare the text in Gradle Console with the above one to check the difference.

like image 26
shingo Avatar answered Oct 17 '22 03:10

shingo


Here are some workaround :

  • Can you please make sure you have multiDexEnabled set to true as shown below in your gradle settings:

    android {
        defaultConfig {
            multiDexEnabled true
        }
    }
    
  • Try android.enableD8 = false in grade.properties
like image 2
youpilat13 Avatar answered Oct 17 '22 03:10

youpilat13


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. Thats where the Program type already present came from.


How I fixed it

  • Exclude the api project from the app project
  • Build api seperately
  • Take the resulting .jarfile and put it in a folder inside your app folder called libs
  • Put this line in your gradle dependencies for your build.gradle (Module: app): implementation fileTree(dir: 'libs', include: ['*.jar'])

This included the finished .jarfile in the appproject and now I can build release. This should be done regardless, because the swagger client shouldnt 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 dont fully understand why there is this difference between the releaseand the debugbuild. Would be glad for any 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: appthat 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.

However, still no idea why there is a difference between debug and release

like image 1
Benjamin Basmaci Avatar answered Oct 17 '22 03:10

Benjamin Basmaci