Переопределить ресурсы с помощью gradle в зависимости от типа buildType

Я хочу переопределить некоторые строки в файле res/strings.xml с помощью gradle.

Я знаю, что с Android Gradle Plugin 0.7. + позволяет иметь вариант конкретной исходной папки. Но у моего приложения много вкусов, и я не хочу добавлять дополнительные варианты конкретных папок.

ОБНОВЛЕНИЕ 2014-01-17

Что я хочу подробно:

У меня есть некоторые переменные в моих ресурсах, которые зависят только от типа buildType (например, "release" ). Сначала я думал, что мой SOLUTION_1 (переопределить данные после объединения ресурсов) приятно, потому что, если мне нужно изменить эти переменные, мне просто нужно их изменить в файле build.config(всего одно место). Но, как написал Скотт Барта в комментарии ниже, есть несколько веских причин, почему это решение НЕ является хорошей идеей.

Итак, я попробовал другое решение SOLUTION_2 (просто объединить нужные ресурсы) на основе этого проекта GitHub shakalaca, Я думаю, что этот способ более изящный, и у меня все еще есть преимущество просто изменить переменные в одном месте!

SOLUTION_1 (переопределение данных после объединения ресурсов):

Что я сделал в AS 0.4.2:

  • in 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 (просто объедините нужные ресурсы)

  • определить floater для определенного типа 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']
        }
    }
    

Ответ 1

Вам следует попытаться найти решение, которое не связано с написанием какого-либо настраиваемого кода в ваших файлах сборки, особенно кода, который делает сложные вещи с переназначением исходных наборов на лету. Пользовательский 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, основанный на статусе сборки debug/release, и вы можете добавить свой собственный код Java из разных типов сборки, чтобы сделать более значимые вещи, BuildConfig был предназначен для обеспечения небольших функциональных различий между типами построения, в тех случаях, когда сборка отладки может потребовать некоторой расточительной операции для оказания помощи в разработке, например, для более обширной проверки данных или создания более подробного ведения журнала отладки, а также для тех расточительных вещей наилучшим образом оптимизированным из релизов. Сказав это, это может быть подходящий механизм для выполнения того, что вы хотите.
  • Подумайте о том, как использовать ароматы для того, что вы используете сейчас. Концептуально аромат похож на тип сборки, поскольку это другой вариант вашего приложения, которое можно построить; система сборки создаст матрицу вкусов и типов сборки и может создавать все комбинации. Тем не менее, вкусы относятся к другому варианту использования, в котором разные ароматизаторы используют большинство кодов, но могут иметь значительные функциональные различия. Общим примером является бесплатная или платная версия вашего приложения. Поскольку другой ресурс в разных вариантах вашего приложения представляет собой разную функциональность, это может указывать на необходимость различного вкуса. Ароматизаторы могут иметь разные каталоги ресурсов, которые объединены во время сборки так же, как и конфигурации сборки; см. вопрос, связанный выше для получения дополнительной информации.

Ответ 2

Я не считаю, что вам нужно полностью настроить конструкцию script для достижения того, чего вы хотите. Согласно моему чтению 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.

Хотя вы можете настроить имена папок, указав соответствующие исходные коды. [type].res.srcDirs, если вы действительно хотите их изменить.

Ответ 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);