問題說明

在更新到 0.60 之後打包 android release APK 時遇到了這樣的錯誤:Error: Duplicate resources

相關 issue 連結

主要的問題是在進行 react-native bundle 指令時會重複的打包資源,因此在後續打包 APK 時出現錯誤

解決方式

主要的解決方法是在 react-native library 的 react.gradle 檔案中找到 doFirst 程式區塊,並在下方加入一段 doLast 程式碼來避免重複的打包資源

node_modules/react-native/react.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
doFirst {
...
}

/** Add doLast */
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()

File originalDir = file("$buildDir/generated/res/react/release/raw");
if (originalDir.exists()) {
File destDir = file("$buildDir/../src/main/res/raw");
ant.move(file: originalDir, tofile: destDir);
}
}

由於這個方法是直接對 node_module 進行修改,當重新安裝 node_module 時,修改過的設定就會被蓋掉,為了避免重複的修改行為,我們可以利用 script 和 postinstall 設定來自動化。

  1. 建立一個新資料夾 fixAndroid,在資料夾新增一個檔案 android-gradle-fix

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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
  2. fixAndroid 資料夾中再建立一個檔案 android-release-fix.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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)
    }
  3. 修改 package.json 加入 postinstall script

    1
    2
    3
    ...
    "postinstall": "node ./fixAndroid/android-release-fix.js"
    ...

postInstall 指令會在每次 node_module 安裝結束後執行

這樣就能在重新安裝 node_module 後自動重新加入修復的 doLast 程式碼

參考資源: https://gist.github.com/maiquemalmeida/2f0df4a5ab79c9d4a25dc142633ac3c1