Динамическое генерирование продуктов
Я создал приложение для Android, которое необходимо создать во многих (30+) вариантах.
Моя идея состояла в том, чтобы сгенерировать различные productFlavors
непосредственно из структуры папок в моем каталоге src
, так как конфигурация всегда очень похожа (в основном просто другое имя пакета, новый значок запуска и некоторые строки изменяются).
Папка src
выглядит следующим образом:
└── src
├── flavor1
│ ├── flavor2.keystore
│ ├── res
├── flavor2
│ ├── res
│ ├── flavor2.keystore
└── main
├── AndroidManifest.xml
├── java
└── res
Если бы мне пришлось вручную создавать свойства gradle, это выглядело бы так:
android {
....
productFlavors {
flavor1 {
packageName 'com.example.flavor1'
}
flavor2 {
packageName 'com.example.flavor2'
}
}
}
Каждый раз, когда я пытаюсь изменить конфигурацию productFlavors
после ее создания, я получаю либо ошибку, либо изменения/дополнения игнорируются молча.
Это может быть проблемой, вызванной мной, потому что мой опыт gradle/Groovy очень ограничен, или это невозможно.
В основном я получаю ошибку, говоря, что GroupableProductFlavorDsl_Decorated
невозможно манипулировать так, как я хочу.
То, что я пытаюсь заархивировать, должно выглядеть примерно так:
android {
....
def flavors = getMyFlavorsFromFileSystem()
productFlavors {
}
flavors.each { name, config ->
productFlavors[name] << config
}
}
Примечание: Я знаю, что этот вопрос в основном представляет собой дубликат старого вопроса, о котором, к сожалению, никогда не отвечали. Поскольку gradle является новым для Android-мира, я надеюсь получить больше ответов, поскольку с момента последнего вопроса был задан вопрос, потому что теперь больше разработчиков используют gradle.
Update:
Вот некоторые очень простые подходы, которые я пробовал:
Вариант 1:
android {
productFlavors {
}
productFlavors['flavor1'] << {
packageName "com.example.flavor1"
}
productFlavors['flavor2'] << {
packageName "com.example.flavor2"
}
}
/*
A problem occurred evaluating root project 'MyProject'.
> GroupableProductFlavorDsl with name 'flavor1' not found.
*/
Вариант 2:
android {
productFlavors {
}
productFlavors['flavor1'] = {
packageName "com.example.flavor1"
}
productFlavors['flavor2'] = {
packageName "com.example.flavor2"
}
}
/*
no error, but does not work
*/
Вариант 3:
android {
productFlavors {
}
productFlavors['flavor1'] = [packageName: "com.example.flavor1"]
productFlavors['flavor2'] = [packageName: "com.example.flavor2"]
}
/*
no error, but does not work
*/
Все они как Gist.
Ответы
Ответ 1
Решается методом проб и ошибок:
android {
// let assume these are return by a function which reads the filesystem
def myFlavors = [
flavor1: [
packageName: "com.example.flavor1"
],
flavor2: [
packageName: "com.example.flavor2"
]
]
productFlavors {
myFlavors.each { name, config ->
"$name" {
packageName config.packageName
}
}
}
}
Ответ 2
Я знаю, что для этого уже есть ответ, но я как бы комбинировал подход Chris.Zou с TheHippo. И добавьте свой собственный метод для этого.
В принципе, при работе с ароматами мы обычно работаем с разными каталогами в /app/src/, который содержит ресурсы. И поскольку имя каталога равно имени пакета, я просто перечислял каталоги в этой папке (исключая "main" и "androidTest" ).
Итак, вот мой полный, рабочий gradle script:
def map = [:]
new File("./app/src").eachFile() { file->
def fileName = file.getName()
if( fileName == "main" || fileName == "androidTest") {
return
}
map.put(fileName, 'com.mypackagename.' + fileName )
}
productFlavors {
map.each { flavorName, packagename ->
"$flavorName" {
applicationId packagename
}
}
}
Edit:
- Также хотел бы добавить, com.mypackagename в основном является корневым путем для всех вкусов.
- У меня есть отдельный script, который скопирует каталог аромата в папку /app/src/.
Ответ 3
Так как автор вопроса не хотел делиться своим кодом для чтения файлов. Я напишу о том, что я сделал. Я поместил все имена вариантов в файл с именем "app/build_variants.txt", по одной строке для каждого, что-то вроде этого:
flavor1
flavor2
flavor3
flavor4
flavor5
И в "app/build.gradle":
//...other configs...
android {
productFlavors {
new File('app/build_variants.txt').eachLine { "$it" { resValue "string", "build_variant_name", "$it" } }
}
}
//...other configs
Это будет иметь тот же результат, что и следующий код:
//...other configs...
android {
productFlavors {
flavor1 { resValue "string", "build_variant_name", "flavor1" }
flavor2 { resValue "string", "build_variant_name", "flavor2" }
flavor3 { resValue "string", "build_variant_name", "flavor3" }
flavor4 { resValue "string", "build_variant_name", "flavor4" }
flavor5 { resValue "string", "build_variant_name", "flavor5" }
}
}
//...other configs
Ключевым здесь является строка new File('app/build_variants.txt').eachLine { "$it" { resValue "string", "build_variant_name", "$it" } }
. Он читает файл "app/build_variants.txt" и для каждой строки генерирует аромат продукта с именем в строке, которую он читает. $it
- это строка, в которую он проходит. Это просто синтаксис закрытия groovy. Если вы хотите более глубокого понимания. Я настоятельно рекомендую смотреть @видео Daniel Lew о groovy и gradle. Это потрясающе!