При работе над Android-проектом, представляющим собой платформу для создания приложений для просмотра видеоконтента, возникла необходимость динамической настройки продуктовых вкусов с передачей информации о подписании конфигов во внешний файл.
Подробности под катом.
Исходные данные
Существует Android-проект, который представляет собой платформу для создания приложений для просмотра видеоконтента.Кодовая база общая для всех приложений, различия заключаются в настройках параметров REST API и настройках внешнего вида приложения (баннеры, цвета, шрифты и т.д.).
В проекте используются три вкусовых измерения:
- рынок: «Google» или «amazon».
Поскольку приложения распространяются как в Google Play, так и на Amazon Marketplace, возникает необходимость разделения некоторого функционала в зависимости от места распространения.
Например: Amazon запрещает использование механизма Google In-App Purchases и требует внедрения собственного механизма.
- конечная точка: «про» или «постановка».
Особые конфигурации для производственных и промежуточных версий.
- сайт: фактический размер для конкретного приложения.
ApplicationId и SigningConfig установлены.
Проблемы, с которыми мы столкнулись
При создании нового приложения необходимо было добавить Product Flavor:Также необходимо было добавить соответствующую конфигурацию подписи:application1 { dimension 'site' applicationId 'com.damsols.application1' signingConfig signingConfigs.application1 }
application1 {
storeFile file("path_to_keystore1.jks")
storePassword "password1"
keyAlias "application1"
keyPassword "password1"
}
Проблемы:
- пять строк для добавления одного приложения, отличающегося только applicationId и SigningConfig. Когда количество приложений стало больше 50, файл build.gradle стал содержать более 500 строк информации о приложениях.
- хранение в открытом виде информации о хранилище ключей для подписи приложений.
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 23
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
}
}
flavorDimensions "site", "endpoint", "market"
signingConfigs {
application1 {
storeFile file("application1.jks")
storePassword "password1"
keyAlias "application1"
keyPassword "password1"
}
application2 {
storeFile file("application2.jks")
storePassword "password2"
keyAlias "application2"
keyPassword "password2"
}
application3 {
storeFile file("application3.jks")
storePassword "password3"
keyAlias "application3"
keyPassword "password3"
}
}
productFlavors {
pro {
dimension 'endpoint'
}
staging {
dimension 'endpoint'
}
google {
dimension 'market'
}
amazon {
dimension 'market'
}
application1 {
dimension 'site'
applicationId "com.damsols.application1"
signingConfig signingConfigs.application1
}
application2 {
dimension 'site'
applicationId "com.damsols.application2"
signingConfig signingConfigs.application2
}
application3 {
dimension 'site'
applicationId "com.damsols.application3"
signingConfig signingConfigs.application3
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.
jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
}
Отображение информации о сертификатах
Первым шагом было перенести информацию о сертификатах в отдельный json-файл.Например, информация также хранится в открытом виде, но ничто не мешает вам сохранить файл в зашифрованном виде (мы используем GPG) и расшифровать его непосредственно во время сборки приложения.
Файл JSON имеет следующую структуру: {
"signingConfigs":[
{
"configName":"application1",
"storeFile":"application1.jks",
"storePassword":"password1",
"keyAlias":"application1",
"keyPassword":"password1"
},
{
"configName":"application2",
"storeFile":"application2.jks",
"storePassword":"password2",
"keyAlias":"application2",
"keyPassword":"password2"
},
{
"configName":"application3",
"storeFile":"application3.jks",
"storePassword":"password3",
"keyAlias":"application3",
"keyPassword":"password3"
},
]
}
Удаляем раздел SigningConfigs в файле build.gradle.
Упрощение раздела «Вкусы продуктов»
Чтобы уменьшить количество строк, необходимых для описания варианта продукта с измерением = «сайт», был создан массив с необходимой информацией для описания конкретного приложения, а все варианты продукта с измерением = «сайт» были удалены.
Был: .
productFlavors { pro { dimension 'endpoint' } staging { dimension 'endpoint' } google { dimension 'market' } amazon { dimension 'market' } application1 { dimension 'site' applicationId "com.damsols.application1" signingConfig signingConfigs.application1 } application2 { dimension 'site' applicationId "com.damsols.application2" signingConfig signingConfigs.application2 } application3 { dimension 'site' applicationId "com.damsols.application3" signingConfig signingConfigs.application3 } } } .
Стал: .
productFlavors { pro { dimension 'endpoint' } staging { dimension 'endpoint' } google { dimension 'market' } amazon { dimension 'market' } } def applicationDefinitions = [ ['name': 'application1', 'applicationId': 'com.damsols.application1'], ['name': 'application2', 'applicationId': 'com.damsols.application2'], ['name': 'application3', 'applicationId': 'com.damsols.application3'] ] } .
Динамическое создание вкусов продукта
Последним шагом было динамическое создание разновидностей продуктов и конфигураций подписи с использованием внешнего файла JSON с информацией о сертификатах из массива applicationDefinitions. def applicationDefinitions = [
['name': 'application1', 'applicationId': 'com.damsols.application1'],
['name': 'application2', 'applicationId': 'com.damsols.application2'],
['name': 'application3', 'applicationId': 'com.damsols.application3']
]
def signKeysFile = file('signkeys/signkeys.json')
def signKeys = new JsonSlurper().
parseText(signKeysFile.text)
def configs = signKeys.signingConfigs
def signingConfigsMap = [:]
configs.each { config ->
signingConfigsMap[config.configName] = config
}
applicationDefinitions.each { applicationDefinition ->
def signingConfig = signingConfigsMap[applicationDefinition['name']]
android.productFlavors.create(applicationDefinition['name'], { flavor ->
flavor.dimension = 'site'
flavor.applicationId = applicationDefinition['applicationId']
flavor.signingConfig = android.signingConfigs.create(applicationDefinition['name'])
flavor.signingConfig.storeFile = file(signingConfig.storeFile)
flavor.signingConfig.storePassword = signingConfig.storePassword
flavor.signingConfig.keyAlias = signingConfig.keyAlias
flavor.signingConfig.keyPassword = signingConfig.keyPassword
})
}
Чтобы добавить чтение из зашифрованного хранилища, необходимо заменить раздел
def signKeysFile = file('signkeys/signkeys.json')
def signKeys = new JsonSlurper().
parseText(signKeysFile.text)
def configs = signKeys.signingConfigs
для чтения из зашифрованного файла.
build.gradle целиком import groovy.json.JsonSlurper
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 23
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
}
}
flavorDimensions "site", "endpoint", "market"
signingConfigs {}
productFlavors {
pro {
dimension 'endpoint'
}
staging {
dimension 'endpoint'
}
google {
dimension 'market'
}
amazon {
dimension 'market'
}
}
}
def applicationDefinitions = [
['name': 'application1', 'applicationId': 'com.damsols.application1'],
['name': 'application2', 'applicationId': 'com.damsols.application2'],
['name': 'application3', 'applicationId': 'com.damsols.application3']
]
def signKeysFile = file('signkeys/signkeys.json')
def signKeys = new JsonSlurper().
parseText(signKeysFile.text) def configs = signKeys.signingConfigs def signingConfigsMap = [:] configs.each { config -> signingConfigsMap[config.configName] = config } applicationDefinitions.each { applicationDefinition -> def signingConfig = signingConfigsMap[applicationDefinition['name']] android.productFlavors.create(applicationDefinition['name'], { flavor -> flavor.dimension = 'site' flavor.applicationId = applicationDefinition['applicationId'] flavor.signingConfig = android.signingConfigs.create(applicationDefinition['name']) flavor.signingConfig.storeFile = file(signingConfig.storeFile) flavor.signingConfig.storePassword = signingConfig.storePassword flavor.signingConfig.keyAlias = signingConfig.keyAlias flavor.signingConfig.keyPassword = signingConfig.keyPassword }) } dependencies { implementation fileTree(dir: 'libs', include: ['*.
jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
}
Ссылка на Гитхаб Спасибо!
Теги: #Android #Разработка Android #Разработка мобильных приложений #gradle #динамические варианты
-
Советы По Покупке Нового Ноутбука
19 Oct, 24 -
Странности В Доставке
19 Oct, 24 -
«Сервер» Для Кочевой Жизни
19 Oct, 24 -
Страна Чудес – Создайте Свою Вторую Жизнь
19 Oct, 24 -
Поддержка Soap-Интерфейса В Яндекс.спеллере
19 Oct, 24