Я разработал приложение, в котором используется 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". Это нормально?
ProGuard упоминается в комментариях, что может быть проблемой. Это единственный случай, который мне удалось найти, ссылаясь на него. В моем build.gradle для проекта "приложение" есть эта часть:
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
Однако его удаление ничего не изменило.
При поиске "io.swagger" AS находит только следующее:
При поиске "io.swagger.client.ApiCallback" AS находит только следующее:
Как было предложено, я попытался добавить
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'
}
@JakeSteam, как я уже сказал, я новичок в gradle, но я обновил вопрос, чтобы адресовать ProGuard
Можете ли вы опубликовать свой файл Gradle?
@SharpMobileCode Готово
Caused by: com.android.tools.r8.utils.AbortException: Error: Program type already present: io.swagger.client.ApiCallback
Означает, что существует несколько зависимостей, содержащих этот класс, возможно, разных версий, поэтому вам нужно исключить их.
Дважды нажмите shift, найдите io.swagger, затем запишите найденные файлы jar.
Если эти файлы jar просто дублируются, оставьте 1 в build.gradle.
Если эти jar-файлы являются модулями другой зависимости, введите gradlew -q app:dependencies в терминале, найдите имена корневых зависимостей.
В 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", но не понимаю, что мне нужно исключить, и как узнать, что мне нужно исключить: /
Только один результат на шаге 1!? Можете поделиться скриншотом или тем файлом? и устанавливаете ли вы разные зависимости между выпуском и отладкой?
Единственная разница, которую я вижу, где конкретно упоминаются «выпуск» или «отладка» в любом файле gradle, - это тот, который я предоставил, но, как я уже сказал, его удаление ничего не меняет.
TBH Я вообще понятия не имею, я также пытался создать settings.gradle в 2 модулях с тем же именем корневого проекта, без проблем. Я добавил в свой ответ еще одно предложение, которое может помочь отладить эту проблему.
Я новичок и действительно не знаю, как применить ваше предложение обновления. Можете ли вы разбить его немного дальше и показать мне, что именно делать?
Здесь - это обходной путь:
Can you please make sure you have
multiDexEnabledset to true as shown below in your gradle settings:
android {
defaultConfig {
multiDexEnabled true
}
}
android.enableD8 = false в grade.propertiesК сожалению, ни одно из предложений не сработало. android.enableD8even заставил AS выдать предупреждение о том, что он устарел. Я добавлю еще несколько деталей к этому вопросу, поскольку, похоже, это проблема, с которой сталкивались многие. Может, это поможет.
Я нашел ответ на свой вопрос:
Что я сделал, так это создал клиент swagger, переместил созданную папку в мой проект, соответствующим образом скорректировал файл settings.gradle и файл build.gradle.
Что пошло не так
Как я настроил, gradle собирал проект «api», а затем проект app.
По какой-то причине, когда я настраивал вещи как release, проект app ссылался на код проекта api, а также на полученный файл .jar. Вот откуда взялся Program type already present.
Как я это исправил
api из проекта appapi отдельно.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.
Быстрый вопрос, исчезнет ли проблема при отключении ProGuard? Если да, то в настройки ProGuard нужно добавить некоторые исключения!