Occasus
Occasus

Reputation: 121

React Native Error: Multiple dex files define Lcom/facebook/binaryresource/BinaryResource while generating a production apk

I'm getting the following exception while trying to package a React Native project into a production apk (running gradlew assembleRelease with the config taken from here):

* What went wrong:
Execution failed for task ':app:transformDexWithDexForRelease'.
> com.android.build.api.transform.TransformException: 
com.android.ide.common.process.ProcessException: 
java.util.concurrent.ExecutionException: com.android.dex.DexException: 
Multiple dex files define Lcom/facebook/binaryresource/BinaryResource;

While attempting to package for debug, app appears to be working perfectly on an emulator (either by running react-native run-android or by executing gradlew assembleDebug).

This is happening on a project that used to package correctly (production apk as well), but has not been built for a while due to iOS-focused development (we were only running react-native run-android every once in a while to make sure nothing is broken, apparently that's not enough). During this time, the Android configuration was not touched or changed at all, all changes were done either to the iOS app or to the Javascript code (a few new npm packages were installed, but they are all pure JS and do not require any linking).

I have uploaded the full gradle stack trace here.

I'm using the latest react-native version (0.55.4) and react version 16.3.2. Have also tried updating various packages, and running gradlew clean.

Would appreciate any help with the matter. I'm happy to provide any configuration segments that may be relevant, I just didn't know what to provide as no android-related config/code was changed since the last time it was working.

Thanks!

Upvotes: 1

Views: 853

Answers (3)

staafl
staafl

Reputation: 3225

Here's a method that can help with diagnosing the problem.

A bit before the final error message you'll get the list of JAR files that Gradle is trying to merge (usually in android\app\build\intermediates\transforms\preDex\release). Each of these JAR files comes from some dependency of your project.

You can unzip the JAR files and look for the name of the class that's causing the conflict. In my case, it was INotificationSideChannel, in the asker's it was BinaryResource.

! grep INotificationSideChannel * -irl
50/classes.dex
80/classes.dex

So the conflict is between 50.jar and 80.jar. Looking inside the extracted directories, there's the 50/META-INF/com.android.support_support-compat.version and 80/META-INF/androidx.core_core.version files, which indicate that these files represent the com.android.support:support-compat and androidx.core:core dependencies respectively.

For the record, AndroidX is meant as a replacement to the Android Support Library, so it makes sense that having both of them in the build would cause a conflict. Time to check the project dependency tree:

! gradlew app:dependencies

Going through the list I can see that androidx is only required once, through the com.google.android.gms:play-services-gcm:17.0.0 dependency, which in turn is referenced by react-native-device-info.

A little Googling indicates that play-services-gcm used to reference com.android.support in 16.1.0, which led me to the following snippet in android/build.gradle which solved the problem for me:

subprojects {
  project.configurations.all {
     resolutionStrategy.eachDependency { details ->

        // normalize com.android.support library versions
        if (details.requested.group == 'com.android.support'
              && !details.requested.name.contains('multidex') ) {
           details.useVersion "28.0.0"
        }

        // downgrade play-services-gcm version to avoid referencing androidx
        if (details.requested.group == 'com.google.android.gms' &&
            details.requested.name == 'play-services-gcm') {
           details.useVersion "16.1.0"
        }
     }
  }
  ...

In case your conflicting JARs don't have .version files, you can try to identify them from the class folder structure in the jar (e.g. okhttp3/internal will lead you to okhttp3), the name of the conflicting class in the Gradle log, or you can delete the two JARs and look for clues in the log.

Upvotes: 1

Mahdi-Malv
Mahdi-Malv

Reputation: 19220

The error Multiple dex files define ... happens when you have MultiDex and Duplication of libraries..

Let's say you have added a support-v4 library via aar file and also using the gradle.

This'll cause the gradle to add both and that error occurres.

You should look for the library that might have been added twice.

In my case an aar and it's classes.jar both was added while multidex was enabled, so this happened.

Upvotes: 0

Bruno Mazzardo
Bruno Mazzardo

Reputation: 1586

I will let this here for anyone else that might need help.

On android/app/build.gradle, under defaultConfig object put this tag: multiDexEnabled true, if this starts to raise a zip error, put this at the end of the file

configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        def requested = details.requested
        if (requested.group == 'com.android.support') {
            if (!requested.name.startsWith("multidex")) {
                details.useVersion '27.1.0'
            }
        }
    }
}   

This worked for me when I had a multiDex problem, may not work for everyone

Upvotes: 5

Related Questions