React Native Android Duplicate file error when generating apk

后端 未结 8 1460
生来不讨喜
生来不讨喜 2020-11-30 20:30

When I am trying to generate android apk by using ./gradlew installRelease, I get this error in console:

~/React-Native/mockingbird/android/app/         


        
相关标签:
8条回答
  • 2020-11-30 21:16

    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.

    0 讨论(0)
  • 2020-11-30 21:19

    A way to remove the error would be to:

    • put your images in the android/app/src/main/res/drawable folder (create a “drawable” folder if one doesn’t exist)
    • delete all the drawable folders that have suffixes (i.e. drawable-hdpi, drawable-mdpi, etc)
    • don’t bundle assets (i.e. don’t run: react-native bundle …)
    • run Gradle’s assembleRelease

    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:

    • open your project in Android studio and navigate to: android/app/src/main/res/drawable
    • right click on the folder and choose: New > Batch Drawable Import
    • select and configure image assets to import

    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.

    0 讨论(0)
  • 2020-11-30 21:21

    Do not run 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.

    0 讨论(0)
  • 2020-11-30 21:21

    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",
    
    
    0 讨论(0)
  • 2020-11-30 21:22

    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.

    1. Create android-react-gradle-fix file
    2. Create script android-release-gradle-fix.js file
    3. 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

    0 讨论(0)
  • 2020-11-30 21:23

    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}");
    
    0 讨论(0)
提交回复
热议问题