71

When I am trying to generate android apk by using ./gradlew installRelease, I get this error in console:

~/React-Native/mockingbird/android/app/build/intermediates/res/merged/release/drawable-mdpi-v4/src_resources_img_loading.gif: error: Duplicate file.
~/React-Native/mockingbird/android/app/build/intermediates/res/merged/release/drawable-mdpi/src_resources_img_loading.gif: Original is here. The version qualifier may be implied.

I tried Build->Clean Project via Android Studio and ran ./gradlew installRelease again; it didn't work either.

Also, I tried deleting the build folder, but it doesn't help either.

A. Mashreghi
  • 1,729
  • 2
  • 20
  • 33
Shongsu
  • 1,013
  • 1
  • 11
  • 23

10 Answers10

147

Give some tips for you, hope it's work.

Update with "react": "16.7.0", "react-native": "0.57.8"

Custom node_modules/react-native/react.gradle to solve the Duplicate file error perfectly. Add following code into currentBundleTask's creation block (after doFirst block)

doLast {
    def moveFunc = { resSuffix ->
        File originalDir = file("${resourcesDir}/drawable-${resSuffix}");
        if (originalDir.exists()) {
            File destDir = file("$buildDir/../src/main/res/drawable-${resSuffix}");
            ant.move(file: originalDir, tofile: destDir);
        }
    }
    moveFunc.curry("ldpi").call()
    moveFunc.curry("mdpi").call()
    moveFunc.curry("hdpi").call()
    moveFunc.curry("xhdpi").call()
    moveFunc.curry("xxhdpi").call()
    moveFunc.curry("xxxhdpi").call()
}

You can create script to do it automatically.

  1. Create android-react-gradle-fix file
  2. Create script android-release-gradle-fix.js file
  3. Update package.json file:

    "scripts": { "postinstall": "node ./android-release-gradle-fix.js" },

That's it! Run npm install to get awesome.

Note: If you run npm install on ci like jenkins, you may get error: postinstall: cannot run in wd %s %s (wd=%s) node => just use npm install --unsafe-perm instead

Nhan Cao
  • 2,496
  • 1
  • 17
  • 13
  • 2
    This solution worked for me, but my question is how does this error happen in the first place since it only happens on my side branch that I share with another developer? – George Miranda May 04 '18 at 18:41
  • 1
    Thank you so much..you saved my ass. Although i have a question, do i need to insert this code everytime after running npm install, Is it a good idea to modify gradle from node_modules? i seriously doubt it – akaMahesh May 09 '18 at 09:34
  • This is in react-native now (I'm on version 0.56.0) but I still have this error. I'm starting to go crazy, this is blocking me for way too long now. – ThaJay Sep 04 '18 at 11:02
  • @ThaJay on 0.56.0 this script does not work, you need to there are lower number of spaces when doing replace regexp and script does not change anythink – bobu Sep 19 '18 at 10:38
  • @bobu I noticed something like that in the code. Scripts that depend on such things are always bad. I eventually just deleted the whole contents of the folder and just put back what I need which is `drawable`, `mipmap`, `raw`, `values`. Notice there is no postfix on any of them. – ThaJay Sep 20 '18 at 11:43
  • There's a [slight change](https://stackoverflow.com/a/52750886/1701465) that needs to be made to this function in order to make the workaround compatible with the latest versions of RN/Gradle. – Mapsy Oct 11 '18 at 01:31
  • 2
    This solution does not work if you are using react native >=0.57, @Cawfree 's solution does – Apperside Oct 21 '18 at 09:51
  • I modified the regex like so `/\s\/\/ Set up inputs and outputs so gradle can cache the result/g)` to make it a bit more robust. – Sumi Straessle Jan 02 '19 at 23:09
  • 2
    this didn't work for me, removing files in `drawable` did. – Sumi Straessle Jan 02 '19 at 23:59
  • @Apperside, what is Cawfree's solution, can you link me to it please. – Nouman Tahir Jan 12 '19 at 15:50
  • This solution is too extreme. You can't modify `node_modules` folder files. If you feel like you have to, it means you're doing something wrong. Next answer in this thread is more like it: https://stackoverflow.com/a/49589616/925307 – eightyfive Jun 28 '19 at 08:38
  • Yey, it worked! Thank you so much. this was the most frustrating issue – Aamin Khan Oct 02 '19 at 10:32
  • I am using react-native 0.61.1. And getting the same error as Duplicate resource. Anyone of you have any idea how to fix it.? – vicky keshri Dec 27 '19 at 07:20
39

At the time of writing, the more recent versions of React Native (>0.57.0) have increased the Gradle wrapper level to 4.4 and the Gradle plugin to 3.1.4, as indicated by the changelog. This has the effect of making the Gradle build process store the results of AAPT, which are now required, within a different directory than previously.

In terms of Nhan Cao's awesome workaround, we need to make a slight modification to prevent duplicate resource collisions, since it looks to be pointed at the old directory and not the app's generated directory. By changing the target directory where these duplicate files are merged together after the resources have been generated, we can still dedup the resources.

The existing react.gradle refers to the path below:

$buildDir === <project-working-directory>/android/app/build

The duplicate file paths can appear between:

file("$buildDir/../src/main/res/drawable-${resSuffix}")
file("$buildDir/generated/res/react/release/drawable-${resSuffix}")

As a workaround, we can update Nhan's solution as follows (be sure to include this within the currentBundleTask after the declaration of doFirst in react.gradle:

doLast {
    def moveFunc = { resSuffix ->
        File originalDir = file("$buildDir/generated/res/react/release/drawable-${resSuffix}");
        if (originalDir.exists()) {
            File destDir = file("$buildDir/../src/main/res/drawable-${resSuffix}");
            ant.move(file: originalDir, tofile: destDir);
        }
    }
    moveFunc.curry("ldpi").call()
    moveFunc.curry("mdpi").call()
    moveFunc.curry("hdpi").call()
    moveFunc.curry("xhdpi").call()
    moveFunc.curry("xxhdpi").call()
    moveFunc.curry("xxxhdpi").call()
}

If your app depends on /raw assets too, the method outlined below should help you:

doLast {
    def moveFunc = { resSuffix ->
        File originalDir = file("$buildDir/generated/res/react/release/${resSuffix}");
        if (originalDir.exists()) {
            File destDir = file("$buildDir/../src/main/res/${resSuffix}");
            ant.move(file: originalDir, tofile: destDir);
        }
     }
     moveFunc.curry("drawable-ldpi").call()
     moveFunc.curry("drawable-mdpi").call()
     moveFunc.curry("drawable-hdpi").call()
     moveFunc.curry("drawable-xhdpi").call()
     moveFunc.curry("drawable-xxhdpi").call()
     moveFunc.curry("drawable-xxxhdpi").call()
     moveFunc.curry("raw").call()
}

If your app also uses a custom "build types" other than release, such as preRelease or stagingRelease(Android Gradle Plugin allows you to define them in build.gradle), you should change originalDir variable like below:

# from
File originalDir = file("$buildDir/generated/res/react/release/drawable-${resSuffix}");
# to 
File originalDir = file("$buildDir/generated/res/react/${targetName}/drawable-${resSuffix}");
Jatin Bhuva
  • 1,301
  • 1
  • 4
  • 22
Mapsy
  • 4,192
  • 1
  • 37
  • 43
  • 2
    For anyone who build an apk with productFlavors the originalDir in this answer should update accordingly; for example mine would be "$buildDir/generated/res/react/develop/release/drawable-${resSuffix}" – siripan Nov 22 '18 at 08:53
  • 1
    wow, thank you for saving my day, that's really helpful and appreciative – Ayush Katuwal Jan 23 '22 at 17:08
37

Remove the files you might have on:

android/app/src/main/res/drawable-mdpi/
android/app/src/main/res/drawable-xhdpi/
android/app/src/main/res/drawable-xxhdpi/

Run Build again, This fixed the issue for me.

MOSTRO
  • 409
  • 4
  • 5
  • 2
    I cleaned out the whole `res/` folder and backed it up. Then I only added `mipmap/ic_launcher.png` and `values/` with `strings.xml` and `styles.xml`. Now it finally works after the most frustrating day and a half this year. – ThaJay Sep 04 '18 at 11:32
  • 3
    its works perfect for me... i am resolving from last one day.. thnx a lot – Mandar Belkunde Oct 04 '18 at 06:18
  • 3
    Thanks, that fixed it for me. I normally only build from the command line, and when opening the project in Android Studio I didn't notice it created these directories and thus duplicate resources. – laurent Oct 14 '18 at 18:48
  • "Android resource linking failed" :/ – matt Jul 16 '19 at 09:27
  • For me works - remove folder `android/app/src/main/res/raw` – Oleg Reym Jul 22 '20 at 12:20
20

Do not run react-native bundle prior to ./gradlew assembleRelease.

For myself, I was running react-native bundle prior to running ./gradlew assembleRelease.

I was getting a similar duplicate error message with one of my assets.

Looking at the ./gradlew assembleRelease output, I could tell that it builds the JS bundle by itself (thanks to apply from: "../node_modules/react-native/react.gradle" in your build.gradle file) so it was not necessary to manually run react-native bundle before hand.

If I simply did not run react-native bundle before running ./gradlew assembleRelease everything worked great.

I tested the Release APK and the JS bundle loads fine, including all images.

My only concern is whether the source maps --sourcemap-output (for Bugsnag) will be created. If not, I'm sure there's a way to have ./gradlew assembleRelease generate those as well. I just have not tested it yet.

Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
  • Didn't work for me. The accepted answer solved it for me, but had to also put android.enableAapt2=false into the gradle.properties file. – zszep May 04 '18 at 11:46
  • Coming back to my own answer here. This used to fix it but even after remove the bundle prior to `assembleRelease`, it's not longer working on React Native 0.57.8. – Joshua Pinter Mar 19 '19 at 20:26
  • 2
    Hmm, coming back here and it's still the correct answer for me. The only duplicate resource errors I was getting were related to `react-navigation`. We were on an old version, `2.3.1`, and after upgrading to `2.18.3` the error went away. But, bundling the react-native JS before running `./gradlew assembleRelease` still caused the error. – Joshua Pinter Mar 19 '19 at 20:49
  • coming back now, you never need to manually bundle the js, assembleRelease will do it for you, so you are right. (also, you can delete the index.android.bundle, no need for that, only if building via Android Studio) – Hugo Gresse Jul 30 '20 at 15:10
6

In order to get my build working for React Native 0.57.5, I used Mapsy's answer with a minor enhancement. I needed to be able to build for multiple flavors and generally I try to avoid hardcoding things. When looking through my react.gradle file, I found it had the following variable defined:

def resourcesDir = file("$buildDir/generated/res/react/${targetPath}")

So instead of hardcoding the build type/flavor in the path like this:

File originalDir = file("$buildDir/generated/res/react/release/drawable-${resSuffix}");

I instead used the resourcesDir variable to set the originalDir path like this:

File originalDir = file("${resourcesDir}/drawable-${resSuffix}");

As a result, my doLast looks like this:

doLast {
            def moveFunc = { resSuffix ->
                File originalDir = file("${resourcesDir}/drawable-${resSuffix}");
                if (originalDir.exists()) {
                    File destDir = file("$buildDir/../src/main/res/drawable-${resSuffix}");
                    ant.move(file: originalDir, tofile: destDir);
                }
            }
            moveFunc.curry("ldpi").call()
            moveFunc.curry("mdpi").call()
            moveFunc.curry("hdpi").call()
            moveFunc.curry("xhdpi").call()
            moveFunc.curry("xxhdpi").call()
            moveFunc.curry("xxxhdpi").call()
        }
WadeStar
  • 69
  • 1
  • 1
5

For me it work to remove the folder: android/build and run ./gradlew assembleRelease again.

suther
  • 12,600
  • 4
  • 62
  • 99
4

What worked for me was simply adding this extra command in package.json, and use that for building:

    "android-build-release": "cd ./android && rm -rf app/src/main/res/drawable* && ./gradlew app:assembleRelease",

Esben von Buchwald
  • 2,772
  • 1
  • 29
  • 37
  • worked like a charm, on `0.56.0` and up, the selected solution (updating `react.gradle`) is not working. This one really should be selected as the right one – Blue Bot Dec 31 '19 at 14:45
2

1.Delete drawable-xxx folders 2.Delete raw inside src -> main -> res folder then

3.run this command in terminal:

react-native bundle --dev false --platform android --entry-file index.js --bundle-output ./android/app/src/main/assets/index.android.bundle --assets-dest ./android/app/src/main/res_temp

4.then generate signed apk using keystore, alias and passwords using terminal or android studio

Alen.Toma
  • 4,684
  • 2
  • 14
  • 31
Andriya
  • 241
  • 2
  • 14
0

A way to remove the error would be to:

  • put your images in the android/app/src/main/res/drawable folder (create a “drawable” folder if one doesn’t exist)
  • delete all the drawable folders that have suffixes (i.e. drawable-hdpi, drawable-mdpi, etc)
  • don’t bundle assets (i.e. don’t run: react-native bundle …)
  • run Gradle’s assembleRelease

However, this is not an ideal solution because each image will only have one resolution for all device sizes. For images that are too big for a device it leaves downscaling to the device and leads to download size and speed issues, and for images too small for a device it leads to diminished image quality.

One convenient solution I’ve found is to use an Android Studio plugin called Android Drawable Importer. To use it after it is installed:

  • open your project in Android studio and navigate to: android/app/src/main/res/drawable
  • right click on the folder and choose: New > Batch Drawable Import
  • select and configure image assets to import

The plugin will handle the generation of the drawable folders and correct image sizes. Once you’ve imported your image, you should be able to run Gradle’s assembleRelease without the duplicate resources error.

Richard Lovell
  • 848
  • 10
  • 18
0

If anyone else is running into this and the doLast solution isn't working it's because you do not have to run react-native bundle anymore before you run bundleRelease. This took hours of head banging because of how many posts out there I found saying doLast solves it.