当我试图使用./gradlew installRelease
生成android apk时,我在控制台中得到了这个错误:
~/React-Native/mockingbird/android/app/build/intermediates/res/merged/release/drawable-mdpi-v4/src_resources_img_loading.gif: error: Duplicate file.
~/React-Native/mockingbird/android/app/build/intermediates/res/merged/release/drawable-mdpi/src_resources_img_loading.gif: Original is here. The version qualifier may be implied.
我通过Android Studio尝试了Build->Clean Project
,并再次运行了./gradlew installRelease
;它也不起作用。
此外,我尝试删除build
文件夹,但也无济于事。
给你一些提示,希望能奏效。
更新为"react":"16.7.0","react native":"0.57.8">
自定义node_modules/areact native/areact.gradle以完美解决重复文件错误。将以下代码添加到currentBundleTask的创建块(在doFirst块之后)
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()
}
您可以创建脚本来自动执行。
- 创建
android-react-gradle-fix
文件 - 创建脚本
android-release-gradle-fix.js
文件 更新
package.json
文件:"脚本":{"postinstall":"node./android release gradle fix.js">},
就是这样!运行npm install
以获得卓越表现。
注意:如果你像jenkins一样在ci上运行npm install
,你可能会得到错误:postinstall: cannot run in wd %s %s (wd=%s) node
=>只使用npm install --unsafe-perm
而不是
在撰写本文时,React Native的最新版本(>0.57.0)已将Gradle包装级别提高到4.4,Gradle插件级别提高到3.1.4,如变更日志所示。这具有使Gradle构建过程将AAPT的结果存储在与以前不同的目录中的效果,AAPT现在是必需的。
就Nhan Cao令人敬畏的变通方法而言,我们需要进行轻微修改,以防止重复的资源冲突,因为它看起来指向旧目录,而不是应用程序的generated
目录。在生成资源后,通过更改将这些重复文件合并在一起的目标目录,我们仍然可以对资源进行重复数据消除。
现有的react.gradle
是指以下路径:
$buildDir === <project-working-directory>/android/app/build
重复的文件路径可能出现在以下位置之间:
file("$buildDir/../src/main/res/drawable-${resSuffix}")
file("$buildDir/generated/res/react/release/drawable-${resSuffix}")
作为一种变通方法,我们可以如下更新Nhan的解决方案(请确保在react.gradle
:中声明doFirst
后将其包含在currentBundleTask
中
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()
}
如果您的应用程序也依赖于/raw
资产,下面列出的方法应该会对您有所帮助:
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()
}
如果您的应用程序还使用自定义";构建类型";除了release
,例如preRelease
或stagingRelease
(Android Gradle插件允许您在build.gradle
中定义它们),您应该更改originalDir
变量,如下所示:
# from
File originalDir = file("$buildDir/generated/res/react/release/drawable-${resSuffix}");
# to
File originalDir = file("$buildDir/generated/res/react/${targetName}/drawable-${resSuffix}");
删除您可能在上的文件
android/app/src/main/res/drawable-mdpi/
android/app/src/main/res/drawable-xhdpi/
android/app/src/main/res/drawable-xxhdpi/
再次运行Build,这为我修复了问题。
请勿在./gradlew assembleRelease
之前运行react-native bundle
就我自己而言,在运行./gradlew assembleRelease
之前,我运行的是react-native bundle
。
我的一个资产收到了类似的重复错误消息。
看看./gradlew assembleRelease
的输出,我可以看出它是自己构建JS捆绑包的(多亏了build.gradle
文件中的apply from: "../node_modules/react-native/react.gradle"
),所以不需要手动运行react-native bundle
。
如果我只是在运行./gradlew assembleRelease
之前没有运行react-native bundle
,那么一切都很好。
我测试了Release APK和JS捆绑包负载良好,包括所有图像。
我唯一关心的是是否会创建源映射--sourcemap-output
(用于Bugsnag)。如果没有,我相信有一种方法可以让./gradlew assembleRelease
生成这些。我只是还没有测试过。
为了让我的构建适用于React Native 0.57.5,我使用了Mapsy的答案,并进行了一些小的增强。我需要能够为多种口味构建,通常我会尽量避免硬编码。当查看我的react.gradle
文件时,我发现它定义了以下变量:
def resourcesDir = file("$buildDir/generated/res/react/${targetPath}")
因此,与其在如下路径中硬编码构建类型/风格:
File originalDir = file("$buildDir/generated/res/react/release/drawable-${resSuffix}");
相反,我使用resourcesDir
变量来设置originalDir
路径,如下所示:
File originalDir = file("${resourcesDir}/drawable-${resSuffix}");
因此,我的doLast
看起来是这样的:
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()
}
对我来说,删除文件夹android/build
并再次运行./gradlew assembleRelease
是可行的。
对我有用的是在package.json
中添加这个额外的命令,并使用它来构建:
"android-build-release": "cd ./android && rm -rf app/src/main/res/drawable* && ./gradlew app:assembleRelease",
1.删除可绘制的xxx文件夹2.删除原始内部src->main->res文件夹然后
3.在终端中运行此命令:
react-native bundle --dev false --platform android --entry-file index.js --bundle-output ./android/app/src/main/assets/index.android.bundle --assets-dest ./android/app/src/main/res_temp
4.然后使用密钥库生成签名的apk,使用终端或安卓工作室生成别名和密码
删除错误的方法是:
- 将您的图像放在android/app/src/main/res/drawable文件夹中(如果不存在,请创建一个"drawable"文件夹)
- 删除所有带有后缀的可绘制文件夹(即可绘制hdpi、可绘制mdpi等)
- 不捆绑资产(即不运行:react native bundle…)
- 运行Gradle的assemblyRelease
但是,这不是一个理想的解决方案,因为对于所有设备尺寸,每个图像都只有一个分辨率。对于对于设备来说太大的图像,它将缩小尺寸留给设备,并导致下载大小和速度问题;对于对于设备而言太小的图像,则会导致图像质量下降。
我发现的一个方便的解决方案是使用一个名为Android Drawable Importer的Android Studio插件。安装后使用:
- 在Android工作室中打开您的项目,并导航到:Android/app/src/main/res/drawable
- 右键单击文件夹,然后选择:新建>批量绘图导入
- 选择并配置要导入的图像资源
该插件将处理可绘制文件夹和正确图像大小的生成。导入映像后,您应该能够运行Gradle的assemblyRelease,而不会出现重复资源错误。
如果有其他人遇到这种情况,而doLast解决方案不起作用,那是因为在运行bundleRelease之前,您不必再运行react原生bundle了。这花了几个小时的时间,因为我发现有很多帖子说doLast解决了这个问题