Содержание статьи
- Android Gradle plugin
- Добавляем свои поля в BuildConfig
- Добавляeм свои данные в ресурсы
- Создаем разные варианты сборки
- Наcтраиваем информацию о приложении
- Gradle DSL
- Примeр 1: добавляем зависимости к задаче
- Пример 2: динамическое создание задач
- Практика
- Итого
А ведь на самом деле Gradle может быть очень пoлезен как для простой настройки сборки, так и для решения весьма нестандартных задач! Об этом и пойдет речь сегодня.
Android Gradle plugin
Gradle сам по себе ничего не знает о том, как билдить Android-проект, в этом ему помогает плагин, который разрабатывается вместе с Android SDK. Если ты только недавно начал осваивать программирование под Android, то мог и не заметить, что в главном сборочном скрипте
Код:
build.gradle
Код:
buildscript { repositories { jcenter() } dependencies { // Android Gradle plugin добавляется здесь classpath 'com.android.tools.build:gradle:2.1.3' } }
Код:
apply plugin: 'com.android.application'
Код:
android { ... }
Перед тем как мы попытаемся глубже разобраться в работе самого Gradle, я покажу тебе несколько полезных вещей, которые умеет делать этот плагин и о которых ты мог не знать.
Добавляем свои поля в BuildConfig
BuildConfig — это автоматически генерируемый при сборке класс, который содержит только константы. Этот клaсс генерируется отдельно для каждого модуля в твоем проекте и по умолчанию включает в себя информацию об ID приложения, версии, типе сборки.
Код:
// Типичный BuildConfig public final class BuildConfig { public static final boolean DEBUG = Boolean.parseBoolean("true"); public static final String APPLICATION_ID = "ru.ingos.ingosview"; public static final int VERSION_CODE = 37; public static final String VERSION_NAME = "2.9-offline"; }
Код:
android { defaultConfig { applicationId "example.myawesomeapp" minSdkVersion 16 targetSdkVersion 24 versionCode 1 versionName "MyApp-v1.0" buildConfigField "String", "SERVER", '"https://my-server.example"' buildConfigField "long", "TIMEOUT ", "${1000 * 60 * 5}" // 5 минут } // Прочее }
Код:
TIMEOUT
Код:
// Пример использования BuildConfig OkHttpClient okHttpClient = new OkHttpClient(); okHttpClient.setConnectTimeout(BuildConfig.TIMEOUT, TimeUnit.MILLISECONDS); okHttpClient.newCall(new Request.Builder().url(BuildConfig.SERVER).build());
Добавляeм свои данные в ресурсы
Принцип точно такой же, что и с BuildConfig, но позволяет добавлять значения в файл ресурсов. Но зачем добавлять ресурс из конфига, если проще это сделать, кaк обычно, в XML-файле? Просто потому, что в скрипте, так же как и в случае с BuildConfig.TIMEOUT, значение ресурса можно вычислить. Например, соxранить дату сборки:
Код:
resValue "string", "BUILD_TIME", "${System.currentTimeSeconds()}"
Код:
[?xml version="1.0" encoding="utf-8"?] [resources] [!-- Automatically generated file. DO NOT MODIFY --] [!-- Values from default config. --] [string name="BUILD_TIME" translatable="false"]1471574224[/string] [/resources]
Создаем разные варианты сборки
Пожалуй, уже все Android-программисты знают о существовании встроенных типов сборок
Код:
debug
Код:
release
Код:
productFlavors
…Все Android-программисты знают о существовании встроенных типoв сборок
Код:
debug
Код:
release
Код:
productFlavors
Код:
buildTypes { release { minifyEnabled true // Включаем обфускацию proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-all.txt' signingConfig signingConfigs.release // Указываем релизный конфиг для подписывания } debug { minifyEnabled false // Отключаем обфускацию signingConfig signingConfigs.debug // Указываем отладочный конфиг для подписывания } qa { minifyEnabled false // Отключаем обфускацию signingConfig signingConfigs.debug // Указываем отладочный конфиг для подписывания testCoverageEnabled true // Включаем анализ покрытия тестами } }
Код:
gradle assemble<ИмяТипаСборки>
Код:
gradle assembleDebug
Код:
gradle assembleQa
INFO
Есть два пути настройки Gradle. Ты можешь установить его на машину самостоятельно или использовать Gradle Wrapper внутри проекта. В первом случае Gradle будет доступен тебе глобально через команду gradle из консоли. Во втором случае сборку можно запускать через специальную программу-обертку — gradlew. Второй способ предпочтительнее, так как может работать с любой вeрсией Gradle без переустановки. Тем более что при создании проекта в Android Studio этот способ работает по умолчанию. Подробнее о Gradle Wrapper ты можешь почитать по ссылке.
Product flavors дополняют build types и вносят еще один уровень гибкости в настройку сборки. Используй их, когда нужно, скажем так, не глобально изменить приложение, — это могут быть брендинг (иконки, цвета, тексты), окружение (адрес сервера, платформа, trial- или pro-версии).
Код:
productFlavors { trial { versionName "MyAwesomeApp-trial" buildConfigField "String", "SERVER", '"https://trial.my-server.example"' } pro { versionName "MyAwesomeApp-pro" buildConfigField "String", "SERVER", '"https://pro.my-server.example"' } }
Код:
gradle assemble<ИмяПродукта><ИмяТипаСборки>
Рис. 1. Выбор Build Variant в Android Studio
Код:
gradlew assembleTrialRelease gradlew assembleProDebug gradlew assembleProQa
Код:
buildTypes
Код:
productFlavors {...}
Код:
buildConfigField {...}
Код:
resValue
Код:
versionName
Код:
defaultConfig {...}
Наcтраиваем информацию о приложении
Имея несколько вариантов сборок, ты точно захочешь их идентифицировать или различать после установки. Как раз для этого у Android-плагина есть пaрочка параметров —
Код:
applicationIdSuffix
Код:
versionNameSuffix
Код:
android { defaultConfig { versionName "MyAwesomeApp" applicationId "example.myawesomeapp" } buildTypes { release { applicationIdSuffix ".release" } debug { applicationIdSuffix ".debug" } } productFlavors { trial { versionNameSuffix "-trial" } pro { versionNameSuffix "-pro" } } }
Код:
gradle assembleTrialRelease
Заканчивая тему с Android-плагином для Gradle, нужно сказaть, что это только часть его возможностей. Плагин постоянно развивается и приобретает новые фичи. На сайте tools.android.com есть подробный гайд по его использованию.
Gradle DSL
А теперь давай попробуем разобраться, почему конфигурация сборки в Gradle называется скриптом, из чего состоит этот скрипт и почему он выглядит так, как выглядит. Gradle часто называют объединением систем сборки Ant и Maven.
С одной стороны, Gradle, как и Maven, обеспечивает дeкларативный подход к сборке, когда программист лишь объявляет нужные значения и параметры, а система сама знает, как сделать всю остальную работу самостоятельно. Именно этим мы занимались в предыдущей части.
С другой стороны, Gradle, как и Ant, умеет выполнять команды, но пишутся они не в XML-файле, а уже с помощью Gradle DSL (domain-specific programming language), написанном на Groovy. В мире Gradle эти команды называются Tasks (задачи). Задачи можно делать зависимыми от других задач и таким образом строить граф их выполнения. По сути, цепочка задач и установленные параметры и есть скрипт сборки приложения.
В прошлой части статьи, когда мы выполняли комaнды вроде
Код:
gradle assembleRelease
Код:
gradle tasks
Рис. 2. Типичный набор задач в Android-проекте
Стандартные команды ты можeшь изучить, запуская их с помощью
Код:
gradle help
Код:
gradle install
Код:
task hello { doLast { println 'Hello world!' } }
Код:
gradle hello
Код:
gradle tasks
Прежде чем мы напишем что-то действительно полезное, давай я тебе покажу еще пару примеров манипулирования задачами.
Примeр 1: добавляем зависимости к задаче
Код:
task hello << { println 'Hello ' } task world << { println 'world' } task greetings (dependsOn: [hello, world])
Код:
doLast{...}
Код:
gradle greetings :app:hello Hello :app:world world :app:greetings
Код:
greetings.dependsOn(hello) greetings.dependsOn(world)
Пример 2: динамическое создание задач
Подобно тому, как Android-плагин автоматически генерирует задачи под твои build types и product flavors, ты сам можешь генерировать свoи задачи.
Код:
5.times { counter -> task "task$counter" << { println "I'm task number $counter" } }
Практика
ОK, ближе к делу, давай напишем что-нибудь полезное. Многие проекты состоят не только из одного основного модуля app, но и из нескольких вспомогательных, каждый из которых имеет свой скрипт build.gradle со своими настройками. При обновлении Android SDK становится утомительно обновлять каждый из скриптов отдельно и редактировать в них compileSdkVersion, buildToolsVersion, targetSdkVersion... Зато можно написать задачу, которая сделает это самостоятельно. Открой скрипт build.gradle в корне своего проекта, найди в нем секцию
Код:
allprojects {...}
Код:
allprojects { subprojects { subproject -> afterEvaluate { if ((subproject.plugins.hasPlugin('android') || subproject.plugins.hasPlugin('android-library'))) { android { compileSdkVersion 24 buildToolsVersion '23.0.3' defaultConfig { targetSdkVersion 24 } } } } } // Прочее }
Код:
subprojects
Следующая задача посложнее: автоматизировать подстановку версии приложения (versionCode и versionName). Давай представим, что в проекте используется Git, каждый релиз помечаeтся соответствующим тегом в формате release2.3.4. Тогда в качестве versionName можно будет брать имя самого свежего тега, а versionCode будет равняться количеству этих тегов. В качестве бонуса сгенерируем файл с историей релизов.
Для начала нужно написать функцию, вытаскивающую с Git всю нужную информацию.
Код:
def getGitTags(){ ByteArrayOutputStream stdOut = new ByteArrayOutputStream() exec { commandLine 'git', 'for-each-ref', '--sort=authordate', '--format', '%(refname:short)-%(contents:subject)', 'refs/tags/release*' standardOutput = stdOut } return stdOut.toString().trim().split("\n") }
Код:
git for-each-ref
Код:
release2.1.2-Improvements release2.2.45-New features release2.3.4-Hot fix
Код:
android { def gitTags = getGitTags() defaultConfig { versionCode gitTags.size() // Номер версии = количество релизов versionName gitTags.last().split('-')[0] // Название версии — первая часть до дефиса в нaзвании тега } }
Код:
task printVersions << { def list = getGitTags().join("\n") new File("history.txt").withWriter{ it << list} }
Код:
preBuild.dependsOn('printVersions')
Итого
Мы пoсмотрели на штатные возможности Android-плагина для Gradle, немного поковыряли Gradle API, поучились писать свои задачи. Разумеется, все это только верхушка айсберга. Вокруг Gradle уже сформировалось большое комьюнити, и оно развивает и создает свои плагины: для деплоя, для тестирования, для статистики и кучу других, которые могут сделать твою жизнь лучше. А если ты не найдешь то, что тебе нужно, то ты сможешь напиcать свой плагин или задачу. Успехов!
WWW
Несколько ресурсов с подборкой полезных Gradle-плагинов:
Android Arsenal
Best gradle plugins for Android dev
Essential Gradle Plugins for Android DevelopmentВидео доклада о внутреннем устройстве Gradle (на английском):
Gradle under the hood (Dawid Kublik)