DexArchiveMergerException только при выпуске

Я разработал приложение, в котором используется Java-клиент, созданный с помощью swagger. Клиент находится в проекте под названием «api», а приложение находится в проекте под названием «app».

Когда я создаю приложение через Build/Make Project, все работает нормально. Также, когда я пытаюсь запустить приложение на эмулируемом или физическом устройстве Run/Run 'app'. Также работает отладчик. Даже когда я собираю проект через Buid/Generate Signed Bundle/APK и выбираю вариант отладки, он работает.

В настоящее время. Сборка не выполняется, когда я пытаюсь создать подписанную сборку выпуска. Следующие сообщения показывают:

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

Я новичок в разработке с Android Studio и Gradle. Я пробовал некоторые решения в Stack Overflow, которые уже предлагали добавить некоторые библиотеки, но пока ни одно из них не помогло с моей проблемой.

Для меня особенно подозрительно, что последнее сообщение об ошибке указывает на io.swagger.client.ApiCallback.

Может ли это иметь какое-то отношение к тому, что оба settings.gradle для api и app имеют одинаковый контент? Оба выглядят так: rootProject.name = "swagger-java-client". Это единственная строка в файле, но, насколько мне известно, settings.gradle для "приложения" уже содержит это содержимое. Я не помню, как менял его, так что для меня это странно, что он показывает "swagger-java-client". Это нормально?

Обновление 1

ProGuard упоминается в комментариях, что может быть проблемой. Это единственный случай, который мне удалось найти, ссылаясь на него. В моем build.gradle для проекта "приложение" есть эта часть:

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

Однако его удаление ничего не изменило.

Обновление 2

При поиске "io.swagger" AS находит только следующее:

DexArchiveMergerException только при выпуске

При поиске "io.swagger.client.ApiCallback" AS находит только следующее:

DexArchiveMergerException только при выпуске

Обновление 3

Как было предложено, я попытался добавить

android {
    defaultConfig {
        multiDexEnabled true
    }
}

и добавление android.enableD8 = false, но это тоже не помогло.

Вот мои файлы build.gradle (первые, которые вызвали проблемы, без предлагаемых исправлений, которые я пробовал до сих пор.)

build.gradle (Module: api) (генерируется 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 (Модуль: приложение):

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'
}

Быстрый вопрос, исчезнет ли проблема при отключении ProGuard? Если да, то в настройки ProGuard нужно добавить некоторые исключения!

Jake Lee 13.01.2019 23:55

@JakeSteam, как я уже сказал, я новичок в gradle, но я обновил вопрос, чтобы адресовать ProGuard

Benjamin Basmaci 14.01.2019 14:16

Можете ли вы опубликовать свой файл Gradle?

SharpMobileCode 18.01.2019 19:28

@SharpMobileCode Готово

Benjamin Basmaci 19.01.2019 15:37
9
4
3 232
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Означает, что существует несколько зависимостей, содержащих этот класс, возможно, разных версий, поэтому вам нужно исключить их.

  1. Дважды нажмите shift, найдите io.swagger, затем запишите найденные файлы jar.

  2. Если эти файлы jar просто дублируются, оставьте 1 в build.gradle.


  1. Если эти jar-файлы являются модулями другой зависимости, введите gradlew -q app:dependencies в терминале, найдите имена корневых зависимостей.

  2. В build.gradle выберите 1 зависимость, которую нужно сохранить, для других добавьте команду exclude.

Например изменить

implementation 'xxx:xxx'

к

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

ОБНОВИТЬ

Некоторые варианты, которые стоит попробовать:

Измените варианты сборки обоих модулей для выпуска, затем Build \ Build APK.

В случае успеха скопируйте текст из консоли Gradle.

Следующая сборка \ Создать подписанный APK ...

Сравните текст в консоли Gradle с приведенным выше, чтобы проверить разницу.

Я не уверен, что я должен здесь видеть. Консоль в значительной степени охвачена некоторыми деревьями ascii, которые выглядят как зависимости, но, поскольку я новичок, как я уже упоминал в своем исходном сообщении, я действительно не знаю, что искать. Кроме того, поскольку в нем так много текста, терминал растянут до такой степени, что часть вывода уже перезаписана / недоступна. Когда сделал 1., появился только один файл. Так что, я думаю, выбор 3. был правильным. Но я не понимаю, что вы здесь имеете в виду. Я понимаю, что может сделать "exclude", но не понимаю, что мне нужно исключить, и как узнать, что мне нужно исключить: /

Benjamin Basmaci 14.01.2019 20:05

Только один результат на шаге 1!? Можете поделиться скриншотом или тем файлом? и устанавливаете ли вы разные зависимости между выпуском и отладкой?

shingo 15.01.2019 04:34

Единственная разница, которую я вижу, где конкретно упоминаются «выпуск» или «отладка» в любом файле gradle, - это тот, который я предоставил, но, как я уже сказал, его удаление ничего не меняет.

Benjamin Basmaci 15.01.2019 15:30

TBH Я вообще понятия не имею, я также пытался создать settings.gradle в 2 модулях с тем же именем корневого проекта, без проблем. Я добавил в свой ответ еще одно предложение, которое может помочь отладить эту проблему.

shingo 15.01.2019 16:41

Я новичок и действительно не знаю, как применить ваше предложение обновления. Можете ли вы разбить его немного дальше и показать мне, что именно делать?

Benjamin Basmaci 19.01.2019 16:23

Здесь - это обходной путь:

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

    android {
        defaultConfig {
            multiDexEnabled true
        }
    }
    
  • Попробуй android.enableD8 = false в grade.properties

К сожалению, ни одно из предложений не сработало. android.enableD8even заставил AS выдать предупреждение о том, что он устарел. Я добавлю еще несколько деталей к этому вопросу, поскольку, похоже, это проблема, с которой сталкивались многие. Может, это поможет.

Benjamin Basmaci 19.01.2019 15:23
Ответ принят как подходящий

Я нашел ответ на свой вопрос:

Что я сделал, так это создал клиент swagger, переместил созданную папку в мой проект, соответствующим образом скорректировал файл settings.gradle и файл build.gradle.


Что пошло не так

Как я настроил, gradle собирал проект «api», а затем проект app. По какой-то причине, когда я настраивал вещи как release, проект app ссылался на код проекта api, а также на полученный файл .jar. Вот откуда взялся Program type already present.


Как я это исправил

  • Исключить проект api из проекта app
  • Собирайте api отдельно
  • Возьмите полученный файл .jar и поместите его в папку внутри папки app под названием libs.
  • Поместите эту строку в ваш gradle dependencies для вашего build.gradle (Module: app): implementation fileTree(dir: 'libs', include: ['*.jar'])

Это включало готовый файл .jar в проект app, и теперь я могу собрать релиз. Это должно быть сделано независимо, потому что клиент swagger в любом случае не должен редактироваться, поскольку это сгенерированный код. Так что даже для того, чтобы не всегда строить эту часть проекта, это нужно делать.

Однако я до сих пор не совсем понимаю, почему существует разница между release и debugbuild. Буду рад любому пониманию этого.


Обновлено: ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ

Я только что узнал: если бы по какой-то причине я хотел сохранить Module: api в своем проекте, я мог бы сделать это, удалив две зависимости в моем Module: app, которые начинаются с implementation fileTree. Api-проект уже включен в зависимость api project(path: ':api'). Здесь проблема была вызвана строками filetree-tree, поскольку они были избыточными.

Однако до сих пор не понятно, почему есть разница между debug и release.

Другие вопросы по теме