I was trying to create a release apk file from Android but when I create a release apk with PNG image I'm getting Duplicate Resource
error. Initially I thought this is happening because I made a mistake in the existing project but when I created a new project with a single Image
component itself I'm getting the Duplicate Resource
error. Here are the steps I followed
react-native init demo
PNG
image inside the assets folder.Image
component with the above PNG
image.Now bundle it using the cmd
react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res/
Then generate release apk using Generate Signed APK
from Android Studio
.
This will throw the following error:
[drawable-mdpi-v4/assets_mario] /Users/jeffreyrajan/Tutorials/RN/errorCheck/android/app/src/main/res/drawable-mdpi/assets_mario.png [drawable-mdpi-v4/assets_mario] /Users/jeffreyrajan/Tutorials/RN/errorCheck/android/app/build/generated/res/react/release/drawable-mdpi-v4/assets_mario.png: Error: Duplicate resources :app:mergeReleaseResources FAILED FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':app:mergeReleaseResources'. > [drawable-mdpi-v4/assets_mario] /Users/jeffreyrajan/Tutorials/RN/errorCheck/android/app/src/main/res/drawable-mdpi/assets_mario.png [drawable-mdpi-v4/assets_mario] /Users/jeffreyrajan/Tutorials/RN/errorCheck/android/app/build/generated/res/react/release/drawable-mdpi-v4/assets_mario.png: Error: Duplicate resources * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. * Get more help at https://help.gradle.org BUILD FAILED in 22s
Note: When you generate a release apk
without any PNG image you will not get any error, it will create you the release apk
.
Here are the other files code.
App.js
import React, {Component} from 'react'; import {Platform, StyleSheet, Image, View} from 'react-native'; export default class App extends Component { render() { return ( <View style={styles.container}> <Image source={require('./assets/mario.png')} /> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1 }, welcome: { fontSize: 20, textAlign: 'center', margin: 10, }, instructions: { textAlign: 'center', color: '#333333', marginBottom: 5, }, });
package.json
{ "name": "errorCheck", "version": "0.0.1", "private": true, "scripts": { "start": "node node_modules/react-native/local-cli/cli.js start", "test": "jest" }, "dependencies": { "react": "16.6.0-alpha.8af6728", "react-native": "0.57.4" }, "devDependencies": { "babel-jest": "23.6.0", "jest": "23.6.0", "metro-react-native-babel-preset": "0.49.0", "react-test-renderer": "16.6.0-alpha.8af6728" }, "jest": { "preset": "react-native" } }
Any solution for this?
Update:
Here are the other details
classpath 'com.android.tools.build:gradle:3.1.4' ext { buildToolsVersion = "27.0.3" minSdkVersion = 16 compileSdkVersion = 27 targetSdkVersion = 26 supportLibVersion = "27.1.1" } distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
Tried with Android Studio 3.0, 3.0.1, 3.1, 3.1.4 & 3.2
It seems like you have the same image resource in two files OR you added an additional, non-required file to the res folder. Rename or remove the other. You can check the required structure of the res folder and subfolders by downloading a fresh copy of WebViewGold from CodeCanyon.
(UPDATED)
This solution works for me
rm -rf ./android/app/src/main/res/drawable-* rm -rf ./android/app/src/main/res/raw
In my case, build failed because there is a duplicated resources in my Android project (inside android folder), these two lines is necessary to remove duplicated resources, that's it.
After trying a lot of solutions I found only Three solution is working. Here they are
Solution 1:
Clean the drawable
folder from the terminal using Gradle. cd
into the android
folder, then run cmd ./gradlew clean
Solution 2:
After bundling delete the drawable
folder from Android Studio
. You could find this in android/app/src/main/res/drawable
Solution 3:
PLEASE DO NOT USE SOLUTION #2, AS PROPOSED BY THE ORIGINAL AUTHOR! All packages under
node_modules
are generated, and any changes you make will be lost when thereact-native
package is reinstalled / upgraded.
In this solution you no need to delete any drawable folder. Just add the following code in the react.gradle file which you could find under node_modules/react-native/react.gradle
path
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() }
For reference I will add the full react.gradle file code here
import org.apache.tools.ant.taskdefs.condition.Os def config = project.hasProperty("react") ? project.react : []; def cliPath = config.cliPath ?: "node_modules/react-native/local-cli/cli.js" def bundleAssetName = config.bundleAssetName ?: "index.android.bundle" def entryFile = config.entryFile ?: "index.android.js" def bundleCommand = config.bundleCommand ?: "bundle" def reactRoot = file(config.root ?: "../../") def inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"] def bundleConfig = config.bundleConfig ? "${reactRoot}/${config.bundleConfig}" : null ; afterEvaluate { android.applicationVariants.all { def variant -> // Create variant and target names def targetName = variant.name.capitalize() def targetPath = variant.dirName // React js bundle directories def jsBundleDir = file("$buildDir/generated/assets/react/${targetPath}") def resourcesDir = file("$buildDir/generated/res/react/${targetPath}") def jsBundleFile = file("$jsBundleDir/$bundleAssetName") // Additional node and packager commandline arguments def nodeExecutableAndArgs = config.nodeExecutableAndArgs ?: ["node"] def extraPackagerArgs = config.extraPackagerArgs ?: [] def currentBundleTask = tasks.create( name: "bundle${targetName}JsAndAssets", type: Exec) { group = "react" description = "bundle JS and assets for ${targetName}." // Create dirs if they are not there (e.g. the "clean" task just ran) doFirst { jsBundleDir.deleteDir() jsBundleDir.mkdirs() resourcesDir.deleteDir() resourcesDir.mkdirs() } 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() } // Set up inputs and outputs so gradle can cache the result inputs.files fileTree(dir: reactRoot, excludes: inputExcludes) outputs.dir jsBundleDir outputs.dir resourcesDir // Set up the call to the react-native cli workingDir reactRoot // Set up dev mode def devEnabled = !(config."devDisabledIn${targetName}" || targetName.toLowerCase().contains("release")) def extraArgs = extraPackagerArgs; if (bundleConfig) { extraArgs = extraArgs.clone() extraArgs.add("--config"); extraArgs.add(bundleConfig); } if (Os.isFamily(Os.FAMILY_WINDOWS)) { commandLine("cmd", "/c", *nodeExecutableAndArgs, cliPath, bundleCommand, "--platform", "android", "--dev", "${devEnabled}", "--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir, *extraArgs) } else { commandLine(*nodeExecutableAndArgs, cliPath, bundleCommand, "--platform", "android", "--dev", "${devEnabled}", "--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir, *extraArgs) } enabled config."bundleIn${targetName}" || config."bundleIn${variant.buildType.name.capitalize()}" ?: targetName.toLowerCase().contains("release") } // Expose a minimal interface on the application variant and the task itself: variant.ext.bundleJsAndAssets = currentBundleTask currentBundleTask.ext.generatedResFolders = files(resourcesDir).builtBy(currentBundleTask) currentBundleTask.ext.generatedAssetsFolders = files(jsBundleDir).builtBy(currentBundleTask) // registerGeneratedResFolders for Android plugin 3.x if (variant.respondsTo("registerGeneratedResFolders")) { variant.registerGeneratedResFolders(currentBundleTask.generatedResFolders) } else { variant.registerResGeneratingTask(currentBundleTask) } variant.mergeResources.dependsOn(currentBundleTask) // packageApplication for Android plugin 3.x def packageTask = variant.hasProperty("packageApplication") ? variant.packageApplication : tasks.findByName("package${targetName}") def resourcesDirConfigValue = config."resourcesDir${targetName}" if (resourcesDirConfigValue) { def currentCopyResTask = tasks.create( name: "copy${targetName}BundledResources", type: Copy) { group = "react" description = "copy bundled resources into custom location for ${targetName}." from resourcesDir into file(resourcesDirConfigValue) dependsOn(currentBundleTask) enabled currentBundleTask.enabled } packageTask.dependsOn(currentCopyResTask) } def currentAssetsCopyTask = tasks.create( name: "copy${targetName}BundledJs", type: Copy) { group = "react" description = "copy bundled JS into ${targetName}." if (config."jsBundleDir${targetName}") { from jsBundleDir into file(config."jsBundleDir${targetName}") } else { into ("$buildDir/intermediates") into ("assets/${targetPath}") { from jsBundleDir } // Workaround for Android Gradle Plugin 3.2+ new asset directory into ("merged_assets/${targetPath}/merge${targetName}Assets/out") { from jsBundleDir } } // mergeAssets must run first, as it clears the intermediates directory dependsOn(variant.mergeAssets) enabled currentBundleTask.enabled } packageTask.dependsOn(currentAssetsCopyTask) } }
Credit: ZeroCool00 mkchx
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With