Несколько Gradle фишек для вашего Android приложения
В одну из последних рассылок Android Weakly попала статья, в которой упомянули интересные особенности организации сборки проекта. После ее прочтения мне захотелось поделиться кое-чем из того, что использую я для настройки сборки Android проекта.
Казалось бы простая идея, однако такой подход используется довольно редко.
Предположим, у вас есть несколько модулей в приложении, в каждом из которых необходимо прописать buildToolsVersion. Часто эту задачу решают путем вынесения конкретной версии в ext-переменную.
Помимо этого, можно оптимизировать код, задав это значение только в одном месте.
Напомню о возможности применения кода из другого gradle файла в вашем build.gradle:
apply plugin: 'com.android.application'
apply from: "$buildSystemDir/application.gradle"
А в файле application.gradle уже можно указать нужные версии:
android {
compileSdkVersion 24
buildToolsVersion "24.0.1"
defaultConfig {
minSdkVersion 15
targetSdkVersion 24
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}
Сюда вы можете вынести любые настройки всего проекта. Затем, в каждом конкретном модуле вы можете переопределить значения (при необходимости).
В отдельный gradle файл можно вынести не только конфигурации android — extension, но и тестовые зависимости, настройку jacoco, findbugs, pmd и т.д. и т.п.
Добавляем свойства в проектВозможно вы уже заметили переменную »$buildSystemDir». Она задана в build.gradle файле корневого проекта:
allprojects {
it.extensions.add("buildSystemDir", "$rootProject.projectDir/buildSystem/")
}
После этого в каждом модуле можно использовать это свойство без всяких префиксов. Что, опять же, чуть-чуть сокращает код ;)Подпись apk
Тема безопасного хранения ключей и паролей от сертификата — очень интересная, и я буду очень рад, если кто-то подскажет хорошее решение (с ci, безопасное), а пока я приведу свой вариант.
Информация о каждом сертификате хранится в *.properties файле. Он может как присутствовать на машине сборки проекта так и отсутствовать.
sign.gradle:
Properties signProp = new Properties()
try {
signProp.load(rootProject.file('signForTest.properties').newDataInputStream())
project.ext {
forTest = new Object() {
def storeFile = signProp.get("forTest.storeFile")
def storePassword = signProp.get("forTest.storePassword")
def keyAlias = signProp.get("forTest.keyAlias")
def keyPassword = signProp.get("forTest.keyPassword")
}
}
} catch (IOException e) {
project.ext {
forTest = new Object() {
def storeFile = "/"
def storePassword = ""
def keyAlias = ""
def keyPassword = ""
}
}
}
android {
signingConfigs {
forTest {
storeFile file(project.ext.forTest.storeFile)
storePassword project.ext.forTest.storePassword
keyAlias project.ext.forTest.keyAlias
keyPassword project.ext.forTest.keyPassword
}
}
}
Вот и всё. Осталось применить этот файл в build.gradle нашего приложения и использовать сконфигурированный signingConfig в нашем buildType.buildSrc
Что же делать, когда в системе сборки нужен полноценный код, а свой плагин писать нет времени? Для этого можно использовать директорию buildSrc.
В корневом каталоге вашего проекта (там где лежит gradlew) создаем новый модуль buildSrc. Этот модуль должен быть обычным java проектом (или groovy или чем-либо ещё), со следующим build.gradle файлом:
apply plugin: "groovy"
repositories {
mavenCentral()
}
dependencies {
compile localGroovy()
compile gradleApi()
}
// START SNIPPET addToRootProject
rootProject.dependencies {
runtime project(path)
}
// END SNIPPET addToRootProject
Теперь в этом проекте можно создать какой-либо класс Awesome.groovy, а в build.gradle Android модуля этот класс импортировать и использовать.
При запуске сборки вашего проекта в первую очередь будет выполнен build модуля buildSrc. После чего произойдет конфигурация остальных модулей.
Не могу сказать, что рекомендую использовать данное решение, т.к. это немного увеличивает время сборки проекта с нуля. Но в крайнем случае может пригодиться.
Flavor dimensionsЭта возможность для конфигурирования проекта хорошо описана в доках (// tools.android.com/tech-docs/new-build-system/user-guide#TOC-Multi-flavor-variants
). Но, либо о ней не все знают, либо не понимают, в каких случаях ее можно использовать, поэтому я решил об этом рассказать.
Предположим, вам необходимо создать платное и бесплатное приложение. При этом ваше приложение выпускается для телевизоров, планшетов и телефонов. Более того, ваше приложение публикуется в разных маркетах.
Добавим в свой build.gradle следующий код:
android {
flavorDimensions "device", "paid", "market"
productFlavors {
tv {
dimension 'device'
}
tablet {
dimension 'device'
}
phone {
dimension 'device'
}
free {
dimension 'paid'
}
premium {
dimension 'paid'
}
google {
dimension 'market'
}
amazon {
dimension 'market'
}
}
}
Объявив 7 flavor-ов в трех разных группах вы получили целых 12 различных вариантов приложения. Добавим сюда еще buildTypes, и мы получим огромное количество apk-шек.
phoneFreeGoogleDebug
phoneFreeGoogleRelease
phoneFreeAmazonDebug
phoneFreeAmazonDebug
phonePremiumGoogleDebug
…. и т.д.
Для каждого объявленного вами flavor создаются свои sourceSet. К примеру, если вы выбрали флавор phoneFreeAmazonDebug будут использованы следующие sourceSets:
src/phoneFreeAmazon
src/phoneFree
src/phoneAmazon
src/freeAmazon
src/phone
src/free
src/amazon
Таким образом, открываются широкие возможности для кастомизации сборок.
Указываем minSdkVersion для buildTypeДля ускорения сборки приложения на этапе разработки часто рекомендуют создать отдельный flavor «develop» и указать для него minSdkVersion = 21. Однако, это не очень удобно, и часто хочется указать этот параметр в buildType debug. Изначально, плагин сборки не позволяет этого сделать, однако, при необходимости, эту проблему можно решить следующим хайком.
Для нужного buildType необходимо добавить ext переменную:
…
buildTypes {
...
debug {
...
ext.minSdkVersion = 21
}
}
Ниже необходимо добавить следующий код:
preBuild.doFirst {
android.applicationVariants.all {
if (it.buildType.hasProperty("minSdkVersion")) {
int i = it.buildType.ext.minSdkVersion;
it.mergedFlavor.setMinSdkVersion(new com.android.builder.core.DefaultApiVersion(i))
}
}
}
Теперь для всех ваших flavor в debug сборке minSdkVersion будет 21. Однако, тут есть жесткая завязка на внутренности плагина, поэтому при обновлении версии плагина что-то может поломаться. Поэтому, не могу рекомендовать использовать этот хак — выбор за вами.
Вместо заключения хочу отметить, что Gradle — очень мощный инструмент для сборки проекта. Если вы уделяете много внимания качеству кода вашего приложения, то не забывайте приводить в порядок и код в build.gradle файлах.