Reputation: 1023
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.
Upvotes: 71
Views: 60124
Reputation: 3
based on @Esben von Buchwald 's answer, i added to the script to delete raw folder also.
"android-build-release": "cd ./android && rm -rf app/src/main/res/drawable* && rm -rf app/src/main/res/raw && ./gradlew app:assembleRelease",
Hope it works for you
Upvotes: 0
Reputation: 21
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.
Upvotes: 0
Reputation: 4262
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}");
Upvotes: 40
Reputation: 241
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
Upvotes: 2
Reputation: 3049
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",
Upvotes: 4
Reputation: 47481
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.
Upvotes: 20
Reputation: 2515
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.
android-react-gradle-fix
fileandroid-release-gradle-fix.js
fileUpdate 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
Upvotes: 147
Reputation: 888
A way to remove the error would be to:
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:
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.
Upvotes: 0
Reputation: 69
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()
}
Upvotes: 6
Reputation: 13578
For me it work to remove the folder: android/build
and run ./gradlew assembleRelease
again.
Upvotes: 5
Reputation: 509
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.
Upvotes: 37