React Native Android Duplicate file error when generating apk
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.
Solution 1:
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.
- Create
android-react-gradle-fix
file - Create script
android-release-gradle-fix.js
file -
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
Solution 2:
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 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 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}");
Solution 3:
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.