【问题标题】:Override resources with gradle depending on buildType根据 buildType 用 gradle 覆盖资源
【发布时间】:2014-02-05 05:22:56
【问题描述】:

我想用 gradle 覆盖我的 res/strings.xml 中的一些字符串。

我知道since Android Gradle Plugin 0.7.+ 有可能拥有一个特定于变体的源文件夹。 但是我的应用程序有很多口味,我不想添加额外的变体特定文件夹。

2014 年 1 月 17 日更新

我想要的详细信息:

我的资源中有一些变量仅取决于 buildType(例如“release”)。 首先,我认为我的 SOLUTION_1(资源合并后覆盖数据)很好,因为如果我必须更改这些变量,我只需在 build.config 中更改它们(只有一个地方)。 但正如 Scott Barta 在下面的评论中所写,有一些很好的理由说明这个解决方案不是一个好主意。

所以我尝试了另一个基于this GitHub project of shakalaca 的解决方案SOLUTION_2(只需合并正确的资源)。我认为这种方式更优雅,而且我仍然有优势,只需在一个位置更改变量!

SOLUTION_1(资源合并后覆盖数据):

我在 AS 0.4.2 中做了什么:

  • build.gradle 中,我尝试将字符串“Hello World”覆盖为“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"
        }
    }
    

复制和过滤任务工作正常,但我无法将“新”values.xml 设置为字符串资源。

SOLUTION_2(只需合并正确的资源)

  • 为特定的 buildType 定义一个风格(例如“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']
        }
    }
    

【问题讨论】:

  • 这通常不是一个好主意。如果您尝试在构建系统中进行重要的操作来修改资源,那么您的整个项目将很难维护和调试。它非常脆弱,因为对插件的更改可能会破坏它,或者对 Android Studio 的更改可能会使其无法使用。值得发布一个问题来准确解释您要完成的工作并列出您考虑过的方法,包括这个。
  • 感谢您的回答。我编辑了我的问题并更详细地解释了我想要什么。我想我为我的问题找到了一个很好的解决方案 (SOLUTION_2)!
  • Scott 是对的 :) 我已经实现了 solution_1,一年后我用更新的 IDE、更新的插件回到我的项目,但它都坏了。

标签: android gradle android-studio android-gradle-plugin build.gradle


【解决方案1】:

您应该努力想出一个不涉及在构建文件中编写任何自定义代码的解决方案,尤其是那些通过动态重新分配源集来完成棘手事情的代码。自定义 Gradle 代码编写起来有点古怪,而且难以调试和维护。新的构建系统非常强大,并且已经具有大量的灵活性,而且您很可能已经可以为所欲为;这只是学习如何做的问题。

尤其是如果您只是了解 Android-Gradle 项目的来龙去脉(而且我们都是新事物),最好先尝试使用系统内置的功能,然后再跳出框框思考。

一些建议:

  • 您不太可能需要根据构建类型改变资源。 Android-Gradle 中的构建类型应该类似于调试或发布,区别在于可调试性、编译器优化或签名;构建类型应该在功能上彼此等效。如果你查看properties you can set on a build type through the Groovy DSL,你可以看到意图:debuggablejniDebugBuildrenderscriptDebugBuildrenderscriptOptimLevelpackageNameSuffixversionNameSuffixsigningConfigzipAlign、@9876534 ,proguardFileproguardFiles
  • 如果您仍然认为要根据构建类型改变资源,使用当前构建系统已经有一种简单的方法可以做到这一点。您可以拥有一个特定于构建类型的资源目录,将您的资源放在那里,构建系统中的资源合并将在构建时为您处理好事情。这是 Android/Gradle 中的强大功能之一。请参阅Using Build Flavors - Structuring source folders and build.gradle correctly,了解如何实现这一目标。
  • 如果您想根据构建类型改变某些内容并且您的需求非常快速和简单,您可能希望在 Java 代码中而不是在资源中而不是在构建系统中进行切换。 BuildConfig 机制可以解决这类事情——它是一个 Java 类,它根据调试/发布构建状态定义了一个 DEBUG 标志,您可以从不同的构建类型添加自己的自定义 Java 代码来做更有意义的事情. BuildConfig 旨在允许构建类型之间存在小的功能差异,适用于调试构建可能希望执行一些浪费的操作来协助开发的情况,例如进行更广泛的数据验证或创建更详细的调试日志记录,而那些浪费的事情是在发布版本中进行了最佳优化。话虽如此,它可能是做你想做的事情的合适机制。
  • 现在考虑为您正在使用的构建类型使用风味。从概念上讲,风味有点像构建类型,因为它是可以构建的应用程序的另一种变体;构建系统将创建风味与构建类型的矩阵,并且可以构建所有组合。但是,风味解决了不同的用例,其中不同的风味共享大多数代码,但可能具有显着的功能差异。一个常见的例子是您的应用程序的免费与付费版本。由于您的应用程序的不同变体中的不同资源代表不同的功能,这可能表明需要不同的风格。风味可以有不同的资源目录,在构建时以与构建配置相同的方式合并;有关详细信息,请参阅上面链接的问题。

【讨论】:

  • 现在可以通过 DSL 生成资源值:plus.google.com/109385828142935151413/posts/UVKA58MZV3J。只是为了兴趣:这应该像BuildConfig 机制一样工作吗?我刚试过这个——gradle没有抛出任何异常,但是我找不到资源值。
  • 值得将其作为一个单独的问题与您正在使用的代码一起发布。我会支持它;-)
  • 是的......你是对的。我将在接下来的几天内写一个新问题。感谢您的支持:)
  • 我的新问题基于 gradle 中的新 resValue 字段:stackoverflow.com/questions/21600160/…
  • 基于构建类型的变体的一个示例:应用程序目标服务器的服务器 IP 和端口,在自定义配置文件中设置(位于 res/xml 文件夹中)。虽然我确信这也可以从应用程序内部以不同的方式实现,而不是通过配置(尽管我更喜欢后者)。
【解决方案2】:

我认为您根本不需要自定义构建脚本来实现您想要的。根据我对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

所以我相信你只要在src/release/res中添加资源就可以实现你想要的。

虽然你可以通过指定相关的 sourceSets.[type].res.srcDirs 来调整文件夹名称,如果你真的想改变它们。

【讨论】:

  • 太棒了! IDE 甚至建议在创建资源文件之前选择构建类型。谢谢
【解决方案3】:

如果有人偶然发现这个

  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);

【讨论】:

  • 这会添加一个字符串常量,但不会像 OP 想要的那样添加一个字符串资源。您可能需要一个资源,例如,从 Manifest 或 xml 文件中引用。
猜你喜欢
  • 2013-10-07
  • 1970-01-01
  • 2015-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多