React Native:错误:重复资源 - Android

问题描述 投票:3回答:5

我试图从Android创建一个发布apk文件,但当我创建一个PNG图像的发布apk时,我得到Duplicate Resource错误。最初我认为这种情况正在发生,因为我在现有项目中犯了一个错误,但当我创建一个单独的Image组件的新项目时,我得到了Duplicate Resource错误。以下是我遵循的步骤

  1. 创建一个应用程序 - react-native init demo
  2. 在项目根文件夹中创建资产文件夹。
  3. 在assets文件夹中添加PNG图像。
  4. 现在使用上面的Image图像实现PNG组件。
  5. 现在使用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/
  6. 然后使用来自Generate Signed APKAndroid 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

react-native android-gradle react-native-android
5个回答
32
投票

在尝试了很多解决方案后,我发现只有两个解决方案正在运行。他们来了

解决方案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


0
投票

使用com.android.tools.build:gradle:3.1.4它应该工作。 RN 0.57在3.2中构建有问题

这个问题可能与以下内容重复:

React Native Error: Duplicate resources, assets coming in some screens and not coming in others in android release APK

如果它仍然不起作用尝试使用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


0
投票

删除您可能拥有的文件:

android / app / src / main / res / drawable -mdpi / android / app / src / main / res / drawable -xhdpi / android / app / src / main / res / drawable-xxhdpi / Run Build,这解决了问题为了我。


0
投票

因此,基本上编辑/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()
}

0
投票

接受的答案将起作用,但是它没有考虑到修改节点包意味着如果更新您的更改将丢失(以及违反最佳实践,您应该以某种方式扩展模块)。

这最初来自React-native android release error: duplicate resource

  1. 在项目的“android”文件夹中创建文件夹“fixAndroid”({project-root} / android / fixAndroid)。
  2. 在项目的“fixAndroid”文件夹中创建文件android-gradle-fix({project-root} / android / fixAndroid / android-gradle-fix)。 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
  3. 在您创建的“fixAndroid”文件夹中创建文件android-release-fix.js: 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) }
  4. 将脚本添加到package.json脚本部分: "postinstall": "node ./android/fixAndroid/android-release-fix.js"

这将找到并将“android-gradle-fix”文件的内容插入到node_modules / react-native / react.gradle中。

  1. 从项目的根目录运行npm install。
  2. 从项目的根目录运行rm -rf android / app / src / main / res / drawable- *。

现在,您可以在控制台或Android Studio上将该版本与React Native捆绑在一起:

React Native命令行

  1. cd {project-root}/android
  2. ./gradlew/bundleRelease

Android Studio

  1. 在Android Studio中打开文件夹android并构建项目。
  2. 选择Build / Generate signed APK to build release。

-4
投票

我在使用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/

问题出在这里。

我解决了这个问题

  1. 删除android/app/res中的重复文件(如果您的资源是从React Native导入的,则可以直接删除res下的目录)。
  2. 删除App/build文件夹。
  3. 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命令

  1. 使用Android Studio的Generate Signed APK正确打包。

以上

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.