我想用gradle覆盖res/strings.xml中的一些字符串。
我知道,自从Android Gradle插件0.7++以来,就有可能拥有一个特定于变体的源文件夹。但我的应用程序有很多风格,我不想添加其他特定于变体的文件夹。
更新2014-01-17
我想要的详细信息:
我的参考资料中有一些变量,它们只取决于buildType(例如"release")。首先,我认为我的SOLUTION_1(在资源合并后覆盖数据)很好,因为如果我必须更改这些变量,我只需要在build.config中更改它们(只需一个位置)。但正如Scott Barta在下面的评论中所写的那样,有一些很好的理由说明这个解决方案不是一个好主意。
因此,我尝试了另一种基于shakalaca的GitHub项目的解决方案solution_2(只需合并正确的资源)。我认为这种方式更优雅,我仍然有优势只需更改一个位置的变量!
SOLUTION_1(资源合并后覆盖数据):
我在AS 0.4.2中做了什么:
-
在
build.gradle
中,我试图将字符串"Hello World"覆盖为"override"(基于我在本文中的回答):android.applicationVariants.all{ variant -> // override data in resource after merge task variant.processResources.doLast { overrideDataInResources(variant) } } def overrideDataInResources(buildVariant){ copy { // *** SET COPY PATHS *** try { from("${buildDir}/res/all/${buildVariant.dirName}") { // println "... FROM: ${buildDir}/res/all/${buildVariant.dirName}" include "values/values.xml" } } catch (e) { println "... EXCEPTION: " + e } into("${buildDir}/res/all/${buildVariant.dirName}/values") // println "... INTO: ${buildDir}/res/all/${buildVariant.dirName}/values" // --- override string "hello_world" filter { String line -> line.replaceAll("<string name="hello_world">Hello world!</string>", "<string name="hello_world">OVERRIDE</string>"); } // *** SET PATH TO NEW RES *** buildVariant.processResources.resDir = file("${buildDir}/res/all/${buildVariant.dirName}/values/values/values.xml") // println "... NEW RES PATH: " + "${buildDir}/res/all/${buildVariant.dirName}/values/values/values.xml" } }
复制和筛选任务运行良好,但我无法将"new"values.xml设置为字符串资源。
SOLUTION_2(只需合并正确的资源)
- 为特定构建类型定义floavor(例如"releaseRes")
-
将这些资源与您想要构建的风格合并:
android.applicationVariants.all{ variant -> variant.mergeResources.doFirst{ checkResourceFolder(variant) } } def checkResourceFolder(variant){ def name = variant.name; if(name.contains("Release")){ android.sourceSets.release.res.srcDirs = ['src/releaseRes/res'] android.sourceSets.flavor1.res.srcDirs = ['src/flavor1/res'] } }
您应该努力想出一个不需要在构建文件中编写任何自定义代码的解决方案,尤其是那些在动态重新分配源集时会遇到棘手问题的代码。自定义Gradle代码编写起来有点古怪,而且很难调试和维护。新的构建系统非常强大,已经有了很大的灵活性,而且很可能你已经可以随心所欲了;这只是一个如何学习的问题。
特别是如果你只是在学习Android Gradle项目的来龙去脉(它对我们来说都是新的),最好在跳出框框思考之前,先努力使用系统内置的功能。
一些建议:
- 您不太可能需要根据构建类型来改变资源。Android Gradle中的构建类型应该是调试或发布,区别在于可调试性、编译器优化或签名;构建类型应该在功能上彼此等效。如果您查看可以通过Groovy DSL在构建类型上设置的属性,您可以看到其意图:
debuggable
、jniDebugBuild
、renderscriptDebugBuild
、renderscriptOptimLevel
、packageNameSuffix
、versionNameSuffix
、signingConfig
、zipAlign
、runProguard
、proguardFile
、proguardFiles
- 如果你仍然认为你想根据构建类型来改变资源,那么在当前的构建系统中已经有一种简单的方法可以做到这一点。您可以有一个特定于构建类型的资源目录,将您的资源放在那里,构建系统中的资源合并将在构建时为您解决问题。这是Android/Gradle中强大的功能之一。请参阅使用Build Flavors-正确构建源文件夹和Build.gradle,了解如何使其发挥作用
- 如果您想根据构建类型进行更改,并且您的需求非常快速和简单,那么您可能希望在Java代码中而不是在资源和构建系统中进行切换。这类事情有
BuildConfig
机制——它是一个Java类,根据调试/发布构建状态定义DEBUG
标志,您可以从不同的构建类型添加自己的自定义Java代码来做更有意义的事情。BuildConfig
旨在允许构建类型之间的微小功能差异,用于调试构建可能想要执行一些浪费的操作来帮助开发的情况,例如进行更广泛的数据验证或创建更详细的调试日志记录,而这些浪费的事情最好在发布构建之外进行优化。话虽如此,这可能是一个合适的机制来做你想做的事 - 考虑对您目前使用的构建类型使用风格。从概念上讲,风味有点像构建类型,因为它是可以构建的应用程序的另一个变体;构建系统将创建风格与构建类型的矩阵,并且可以构建所有组合。然而,风格解决了不同的用例,不同的风格共享大多数代码,但可能存在显著的功能差异。一个常见的例子是您的应用程序的免费版本与付费版本。由于应用程序的不同变体中的不同资源代表不同的功能,这可能表明需要不同的风格。Flavor可以具有不同的资源目录,这些目录在构建时以与构建配置相同的方式进行合并;有关更多信息,请参阅上面链接的问题
我认为您根本不需要自定义构建脚本来实现您想要的。根据我对http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-构建变体;当构建运行时,将从以下文件夹中合并资源(如果存在);
src/[flavour][buildType]/res
src/[buildType]/res
src/[flavour]/res
src/main/res
因此,我相信您只需在src/release/res中添加资源就可以实现您想要的目标。
尽管您可以通过指定相关的sourceSets来调整文件夹名称。[type].res.srcDirs,如果您真的想更改它们。
如果有人偶然发现这个
buildTypes {
debug{
buildConfigField "String", "Your_string_key", '"yourkeyvalue"'
buildConfigField "String", "SOCKET_URL", '"some text"'
buildConfigField "Boolean", "LOG", 'true'
}
release {
buildConfigField "String", "Your_string_key", '"release text"'
buildConfigField "String", "SOCKET_URL", '"release text"'
buildConfigField "Boolean", "LOG", 'false'
}
}
使用构建变体访问这些值:
if(!BuildConfig.LOG)
// do something with the boolean value
或
view.setText(BuildConfig.yourkeyvalue);