我试图从Android创建一个发布apk文件,但当我创建一个PNG图像的发布apk时,我得到Duplicate Resource
错误。最初我认为这种情况正在发生,因为我在现有项目中犯了一个错误,但当我创建一个单独的Image
组件的新项目时,我得到了Duplicate Resource
错误。以下是我遵循的步骤
react-native init demo
PNG
图像。Image
图像实现PNG
组件。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/
Generate Signed APK
的Android Studio
生成release apk。这将抛出以下错误:
[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
注意:当您生成没有任何PNG图像的release apk
时,您将不会收到任何错误,它将为您创建release apk
。
这是其他文件代码。
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"
}
}
对此有何解决方案?
更新:
这是其他细节
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
尝试与Android Studio 3.0, 3.0.1, 3.1, 3.1.4 & 3.2
在尝试了很多解决方案后,我发现只有两个解决方案正在运行。他们来了
解决方案1:
捆绑后从drawable
删除Android Studio
文件夹。你可以在android/app/src/main/res/drawable
找到这个
解决方案2:
在此解决方案中,您无需删除任何可绘制文件夹。只需在react.gradle文件中添加以下代码即可在node_modules/react-native/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()
}
作为参考,我将在此处添加完整的react.gradle文件代码
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)
}
}
图片来源:ZeroCool00 mkchx
使用com.android.tools.build:gradle:3.1.4它应该工作。 RN 0.57在3.2中构建有问题
这个问题可能与以下内容重复:
如果它仍然不起作用尝试使用RN 0.57.2,我正在使用它并且使用这些deps创建版本非常有效:
"dependencies": {
"react": "16.5.0",
"react-native": "0.57.2",
.......
}
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/plugin-proposal-decorators": "^7.0.0",
"@babel/plugin-proposal-do-expressions": "^7.0.0",
"@babel/plugin-proposal-export-default-from": "^7.0.0",
"@babel/plugin-proposal-export-namespace-from": "^7.0.0",
"@babel/plugin-proposal-function-bind": "^7.0.0",
"@babel/plugin-proposal-function-sent": "^7.0.0",
"@babel/plugin-proposal-json-strings": "^7.0.0",
"@babel/plugin-proposal-logical-assignment-operators": "^7.0.0",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
"@babel/plugin-proposal-numeric-separator": "^7.0.0",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
"@babel/plugin-proposal-optional-chaining": "^7.0.0",
"@babel/plugin-proposal-pipeline-operator": "^7.0.0",
"@babel/plugin-proposal-throw-expressions": "^7.0.0",
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
"@babel/plugin-syntax-import-meta": "^7.0.0",
"@babel/plugin-syntax-object-rest-spread": "^7.0.0",
"@babel/plugin-transform-runtime": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-flow": "^7.0.0",
"@babel/register": "^7.0.0",
"babel-core": "^7.0.0-bridge.0",
"babel-preset-react-native-stage-0": "^1.0.1",
.....
}
Gradle deps:
classpath 'com.android.tools.build:gradle:3.1.4'
classpath "io.realm:realm-gradle-plugin:4.0.0"
App gradle
compileSdkVersion 27
buildToolsVersion "27.0.3"
defaultConfig {
applicationId "de.burda.buntede"
minSdkVersion 17
targetSdkVersion 27
Gradle包装道具:
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
删除您可能拥有的文件:
android / app / src / main / res / drawable -mdpi / android / app / src / main / res / drawable -xhdpi / android / app / src / main / res / drawable-xxhdpi / Run Build,这解决了问题为了我。
因此,基本上编辑/node_modules/react-native/react.gradle文件,并在doFirst块之后立即手动添加doLast。
doFirst { ... }
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()
}
接受的答案将起作用,但是它没有考虑到修改节点包意味着如果更新您的更改将丢失(以及违反最佳实践,您应该以某种方式扩展模块)。
这最初来自React-native android release error: duplicate resource
doLast {
def moveFunc = { resSuffix ->
File originalDir = file("${resourcesDir}/drawable-${resSuffix}")
if (originalDir.exists()) {
File destDir = file("${resourcesDir}/drawable-${resSuffix}-v4")
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
const fs = require('fs')
try {
var curDir = __dirname
var rootDir = process.cwd()
var file = `${rootDir}/node_modules/react-native/react.gradle`
var dataFix = fs.readFileSync(`${curDir}/android-gradle-fix`, 'utf8')
var data = fs.readFileSync(file, 'utf8')
var doLast = "doLast \{"
if (data.indexOf(doLast) !== -1) {
throw "Already fixed."
}
var result = data.replace(/\/\/ Set up inputs and outputs so gradle can cache the result/g, dataFix);
fs.writeFileSync(file, result, 'utf8')
console.log('Android Gradle Fixed!')
} catch (error) {
console.error(error)
}
"postinstall": "node ./android/fixAndroid/android-release-fix.js"
这将找到并将“android-gradle-fix”文件的内容插入到node_modules / react-native / react.gradle中。
现在,您可以在控制台或Android Studio上将该版本与React Native捆绑在一起:
React Native命令行
cd {project-root}/android
./gradlew/bundleRelease
Android Studio
我在使用0.57.7后遇到了同样的问题,查看node_modules/react-native/react.gradle
文件,资源输出目录是$buildDir/generated/res/react/${targetPath}
。查看日志中的地址为app/build/generated/res/react/release
,
命令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/
资源输出目录是android/app/src/main/res/
问题出在这里。
我解决了这个问题
android/app/res
中的重复文件(如果您的资源是从React Native导入的,则可以直接删除res下的目录)。App/build
文件夹。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/
换成react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle
不再指定--assets-dest
Run命令
Generate Signed APK
正确打包。以上