Android 根据 buildType 使用 gradle 覆盖资源
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21159484/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Override resources with gradle depending on buildType
提问by owe
I want to override some strings in my res/strings.xml with gradle.
我想用 gradle 覆盖 res/strings.xml 中的一些字符串。
I know that since Android Gradle Plugin 0.7.+theres the possibilty to have a variant specific source folder. But my app has a lot of flavors and I don't want to add additionally variant specific folders.
我知道自从 Android Gradle Plugin 0.7.+ 以来,就有可能拥有一个特定于变体的源文件夹。但是我的应用程序有很多风格,我不想添加额外的变体特定文件夹。
UPDATE 2014-01-17
更新 2014-01-17
What I want in detail:
我想要的详细信息:
I have some variables in my Resources that are depending only by the buildType (e.g. "release"). First I thought my SOLUTION_1(override data after resources were merged) is nice, because if I have to change these variables I just have to change them in the build.config (just one place). But as Scott Barta wrote in the comment below there are some good reasons why this solution is NOT a good idea.
我的资源中有一些仅取决于 buildType(例如“发布”)的变量。首先,我认为我的SOLUTION_1(资源合并后覆盖数据)很好,因为如果我必须更改这些变量,我只需要在 build.config 中更改它们(仅在一个地方)。但正如 Scott Barta 在下面的评论中所写的,有一些很好的理由说明这个解决方案不是一个好主意。
So i tried another solution SOLUTION_2(just merge the right resources) based on this GitHub project of shakalaca. I think this way is more elegant and I still have the advantage just to change the variables in oneplace!
所以我尝试了另一个基于shakalaca GitHub 项目的解决方案 SOLUTION_2(只需合并正确的资源)。我认为这种方式更优雅,我仍然有优势只是在一个地方改变变量!
SOLUTION_1 (override data after resources were merged):
SOLUTION_1(资源合并后覆盖数据):
What I did in AS 0.4.2:
我在 AS 0.4.2 中做了什么:
in
build.gradle
I try to override the string "Hello World" to "OVERRIDE" (based on my answer at this post):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" } }
在
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" } }
The copy and filter task works fine, but I couldn't set the "new" values.xml as string resource.
复制和过滤任务工作正常,但我无法将“新”values.xml 设置为字符串资源。
SOLUTION_2 (just merge the right resources)
SOLUTION_2(只需合并正确的资源)
- define a floavor for specific buildType (e.g. "releaseRes")
merge this resourses with the flavor you want to build:
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'] } }
- 为特定的 buildType 定义一个 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'] } }
采纳答案by Scott Barta
You should strive to come up with a solution that doesn't involve writing any custom code in your build files, especially code that does tricky things with reassigning source sets on the fly. Custom Gradle code is a little funky to write, and it's difficult to debug and maintain. The new build system is extremely powerful and already has tons of flexibility, and it's likely that you can already do what you want; it's just a matter of learning how.
你应该努力想出一个解决方案,它不涉及在你的构建文件中编写任何自定义代码,尤其是那些在动态重新分配源集方面做一些棘手事情的代码。自定义 Gradle 代码编写起来有点时髦,而且很难调试和维护。新的构建系统非常强大,并且已经拥有了大量的灵活性,而且您很可能已经可以做您想做的事情;这只是学习如何做的问题。
Especially if you're just learning the ins and outs of Android-Gradle projects (and it's so new that we all are), it's best to try hard to work with the functionality built into the system before thinking outside the box.
尤其是如果您只是在学习 Android-Gradle 项目的来龙去脉(它是如此新,以至于我们都如此),最好在跳出框框思考之前努力尝试使用系统内置的功能。
Some recommendations:
一些建议:
- It's unlikely you need to vary resources based on build type. A build type in Android-Gradle is supposed to be something like debug or release, where the difference is in debuggability, compiler optimization, or signing; build types are supposed to be functionally equivalent to each other. If you look at the properties you can set on a build type through the Groovy DSL, you can see the intent:
debuggable
,jniDebugBuild
,renderscriptDebugBuild
,renderscriptOptimLevel
,packageNameSuffix
,versionNameSuffix
,signingConfig
,zipAlign
,runProguard
,proguardFile
,proguardFiles
. - If you still think you want to vary resources based on build type, there's already an easy way to do that with the current build system. You can have a build-type-specific resource directory, put your resources in there, and the resource merging in the build system will take care of things for you at build time. This is one of the powerful features in Android/Gradle. See Using Build Flavors - Structuring source folders and build.gradle correctlyfor information on how to make that work.
- If you want to vary something based on build type and your needs are very quick and simple, you might want to do the switch in Java code instead of resources and instead of in the build system. There's the
BuildConfig
mechanism for that sort of thing -- it's a Java class that defines aDEBUG
flag based on debug/release build status, and you can add your own custom Java code from different build types to do more meaningful things.BuildConfig
was intended for allowing small functional differences between build types, for cases where a debug build might want to perform some wasteful operation to assist in development, like doing more extensive data validation or creating more detailed debug logging, and those wasteful things are best optimized out of release builds. Having said that, it might be an appropriate mechanism to do what you want. - Consider using flavors for what you're using build types for now. Conceptually a flavor is kind of like a build type in that it's another variant of your application that can be built; the build system will create a matrix of flavors vs. build types and can build all combinations. However, flavors address a different use case, where different flavors share most code but can have significant functional differences. A common example is a free vs. paid version of your application. Inasmuch as a different resource in different variants of your app represents different functionality, that might indicate a need for a different flavor. Flavors can have different resource directories that are merged at build time in the same way as build configs; see the question linked above for more info.
- 您不太可能需要根据构建类型来改变资源。Android-Gradle 中的构建类型应该是 debug 或 release 之类的东西,区别在于可调试性、编译器优化或签名;构建类型应该在功能上彼此等效。如果您查看可以通过 Groovy DSL 在构建类型上设置的属性,您可以看到意图:
debuggable
,jniDebugBuild
,renderscriptDebugBuild
,renderscriptOptimLevel
,packageNameSuffix
,versionNameSuffix
,signingConfig
,zipAlign
,runProguard
,proguardFile
,proguardFiles
。 - 如果您仍然认为要根据构建类型来改变资源,那么使用当前的构建系统已经有一种简单的方法可以做到这一点。您可以拥有一个特定于构建类型的资源目录,将您的资源放在那里,构建系统中的资源合并将在构建时为您处理。这是 Android/Gradle 的强大功能之一。有关如何使其工作的信息,请参阅使用构建风格 - 正确构建源文件夹和 build.gradle。
- 如果您想根据构建类型改变某些内容并且您的需求非常快速和简单,您可能希望在 Java 代码而不是资源中而不是在构建系统中进行切换。有这种
BuildConfig
机制——它是一个 Java 类,它DEBUG
根据调试/发布构建状态定义一个标志,您可以从不同的构建类型添加自己的自定义 Java 代码来做更有意义的事情。BuildConfig
旨在允许构建类型之间存在小的功能差异,对于调试构建可能想要执行一些浪费操作以帮助开发的情况,例如进行更广泛的数据验证或创建更详细的调试日志记录,而这些浪费的事情最好优化掉发布版本。话虽如此,这可能是一个合适的机制来做你想做的事。 - 考虑为您现在使用的构建类型使用风格。从概念上讲,风味有点像构建类型,因为它是可以构建的应用程序的另一种变体;构建系统将创建一个风格与构建类型的矩阵,并且可以构建所有组合。然而,风格解决了不同的用例,其中不同的风格共享大部分代码,但可能具有显着的功能差异。一个常见的例子是应用程序的免费版本与付费版本。由于应用程序的不同变体中的不同资源代表不同的功能,这可能表明需要不同的风格。Flavors 可以有不同的资源目录,它们在构建时以与构建配置相同的方式合并;有关更多信息,请参阅上面链接的问题。
回答by Jeremy Lakeman
I don't believe you need to customize the build script at all to achieve what you want. According to my reading of http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants; when the build runs, resources will be merged from the following folders, if they exist;
我认为您根本不需要自定义构建脚本来实现您想要的。根据我对http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 的阅读;当构建运行时,资源将从以下文件夹中合并(如果存在);
src/[flavour][buildType]/res
src/[buildType]/res
src/[flavour]/res
src/main/res
So I believe you can achieve what you want by simply add the resources in src/release/res.
所以我相信你可以通过简单地在 src/release/res 中添加资源来实现你想要的。
Though you can tweak the folder names by specifying the relevant sourceSets.[type].res.srcDirs if you really want to change them.
如果你真的想改变它们,你可以通过指定相关的 sourceSets.[type].res.srcDirs 来调整文件夹名称。
回答by Ivan Milisavljevic
If anyone stumble upon this
如果有人偶然发现这个
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'
}
}
And to access those values using build variants:
并使用构建变体访问这些值:
if(!BuildConfig.LOG)
// do something with the boolean value
Or
或者
view.setText(BuildConfig.yourkeyvalue);