Android SDK
Инструменты SDK и сборка. Версии актуальны на август 2026: AGP 9.3, максимальный поддерживаемый API level — 37.
#sdkmanager и avdmanager
Живут в $ANDROID_HOME/cmdline-tools/latest/bin. Если команд нет — в Android Studio доустанавливается пакет «Android SDK Command-line Tools».
export ANDROID_HOME="$HOME/Android/Sdk"
export PATH="$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator"
Минимальный набор переменных. ANDROID_SDK_ROOT устарел в пользу ANDROID_HOME.
sdkmanager --list | head -40
sdkmanager --list_installed
sdkmanager --update
Что доступно и что уже стоит.
sdkmanager "platform-tools" "platforms;android-36" "build-tools;36.0.0"
sdkmanager "system-images;android-36;google_apis;x86_64"
sdkmanager --licenses
Установка компонентов. --licenses нужен в CI, иначе сборка падает на непринятых лицензиях.
yes | sdkmanager --licenses > /dev/null
Автоматическое принятие лицензий для CI.
avdmanager list device | head -30
avdmanager create avd -n test36 -k "system-images;android-36;google_apis;x86_64" -d pixel_7
avdmanager list avd
avdmanager delete avd -n test36
Создание и удаление эмуляторов из консоли.
# образ БЕЗ Google Play — на нём работает adb root
sdkmanager "system-images;android-36;google_apis;x86_64"
# образ С Google Play — root недоступен
sdkmanager "system-images;android-36;google_apis_playstore;x86_64"
Выбор образа определяет, будет ли доступен root.
Для автотестов почти всегда нужен google_apis без Play Store: и root есть, и Google Play Services на месте.
#Эмулятор
emulator -list-avds
emulator -avd test36 &
emulator -avd test36 -no-window -no-audio -no-boot-anim -gpu swiftshader_indirect
Второй вариант — headless-режим для CI. swiftshader_indirect — программный рендер, когда GPU недоступен.
emulator -avd test36 -no-snapshot-load -wipe-data
Чистый старт с нуля. Обязательно перед прогоном, если нужна воспроизводимость.
emulator -avd test36 -writable-system # изменяемый /system
emulator -avd test36 -http-proxy 127.0.0.1:8888
emulator -avd test36 -dns-server 8.8.8.8
Частые флаги. Прокси на уровне эмулятора удобнее, чем через settings put global http_proxy.
adb emu geo fix 37.617 55.755 # долгота, широта
adb emu sms send +79001234567 "код 1234"
adb emu power capacity 15
adb emu kill
Консоль эмулятора через adb: геолокация, входящая SMS, батарея. Так тестируются сценарии, которые на реальном устройстве не воспроизвести.
# хост со стороны эмулятора
10.0.2.2 # localhost машины разработчика
10.0.2.3 # DNS хоста
Специальные адреса эмулятора. Для физического устройства вместо этого — adb reverse.
#Gradle
AGP 9.x требует Gradle 8.11 и новее. Начиная с AGP 9.0 поддержка Kotlin встроена: плагин org.jetbrains.kotlin.android применять больше не нужно.
./gradlew tasks --all | head -40
./gradlew :app:dependencies --configuration releaseRuntimeClasspath
./gradlew :app:assembleDebug --scan
Список задач, дерево зависимостей, build scan с разбором времени сборки.
./gradlew assembleDebug # apk
./gradlew bundleRelease # aab для Play
./gradlew installDebug # собрать и поставить
./gradlew :app:testDebugUnitTest # unit-тесты
./gradlew :app:connectedDebugAndroidTest # инструментальные, нужен девайс
Базовый набор задач.
./gradlew clean build --refresh-dependencies
./gradlew --stop # погасить демонов
rm -rf ~/.gradle/caches/build-cache-1 # снести кеш сборки
Когда «у меня не собирается, а у коллеги собирается». Порядок именно такой: сначала мягкое, потом кеши.
./gradlew assembleDebug --offline
./gradlew assembleDebug -PsomeFlag=true
./gradlew assembleDebug --console=plain # для логов CI
./gradlew assembleDebug --info --stacktrace
Полезные флаги. --console=plain убирает перерисовку прогресса, из-за которой логи CI превращаются в кашу.
// gradle/libs.versions.toml
[versions]
agp = "9.3.0"
kotlin = "2.2.0"
composeBom = "2026.06.01"
[libraries]
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
compose-ui = { group = "androidx.compose.ui", name = "ui" }
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version = "5.0.0" }
[bundles]
compose = ["compose-ui", "compose-material3"]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
Version catalog — четыре секции: [versions], [libraries], [bundles], [plugins]. Файл лежит в gradle/libs.versions.toml.
Дефис в имени алиаса превращается в точку при обращении: compose-ui → libs.compose.ui.
// build.gradle.kts (модуль)
plugins {
alias(libs.plugins.android.application)
// org.jetbrains.kotlin.android НЕ НУЖЕН начиная с AGP 9.0
}
dependencies {
implementation(platform(libs.compose.bom))
implementation(libs.bundles.compose)
implementation(libs.okhttp)
}
Обращение к каталогу: libs.<alias> для библиотек, libs.plugins.<alias> для плагинов, libs.bundles.<alias> для наборов.
android {
namespace = "com.example.app"
compileSdk = 36
defaultConfig {
applicationId = "com.example.app"
minSdk = 24
targetSdk = 36
}
buildFeatures {
compose = true
}
}
Компактный блок. buildFeatures { compose = true } включает Compose; компилятор Compose с Kotlin 2.0 живёт в репозитории Kotlin и поставляется вместе с ним, так что версии всегда совпадают.
Отдельный плагин org.jetbrains.kotlin.plugin.compose нужен только если хочется переопределить версию компилятора или задать опции — он перекрывает координаты, которые AGP подставляет сам.
#Подпись и проверка APK
keytool -genkeypair -v \
-keystore release.jks -alias upload \
-keyalg RSA -keysize 4096 -validity 10000
Создание keystore. validity в днях — для Play нужен запас лет на 25.
keytool -list -v -keystore release.jks -alias upload
keytool -printcert -jarfile app-release.aab
Отпечатки SHA-1 и SHA-256 — нужны для Google Maps, Firebase и App Links.
$ANDROID_HOME/build-tools/36.0.0/apksigner verify --verbose --print-certs app.apk
Проверка подписи и схем (v1/v2/v3/v4). Самый быстрый способ понять, чем подписан чужой или CI-артефакт.
zipalign -c -v 4 app.apk # проверить выравнивание
zipalign -v 4 in.apk out.apk # выровнять
apksigner sign --ks release.jks --ks-key-alias upload out.apk
Порядок важен: сначала zipalign, потом apksigner. Наоборот — сломается подпись.
aapt2 dump badging app.apk | head -20
aapt2 dump permissions app.apk
aapt2 dump xmltree app.apk --file AndroidManifest.xml | head -40
Что внутри APK без распаковки: applicationId, версии, разрешения, манифест. Незаменимо при проверке, что собралось именно то, что ожидалось.
unzip -l app.apk | sort -k1 -rn | head -20
Что занимает место в APK. Быстрая замена APK Analyzer для консоли.
#bundletool
AAB нельзя поставить на устройство напрямую. bundletool превращает бандл в набор APK и умеет ставить нужный вариант.
bundletool build-apks \
--bundle=app-release.aab \
--output=app.apks \
--ks=release.jks --ks-key-alias=upload
Полный набор сплитов для всех конфигураций.
bundletool build-apks \
--bundle=app-release.aab --output=app.apks \
--connected-device \
--ks=release.jks --ks-key-alias=upload
bundletool install-apks --apks=app.apks
Собрать только под подключённое устройство и поставить. Самый короткий путь проверить релизный бандл руками.
bundletool build-apks --bundle=app.aab --output=app.apks --mode=universal
unzip -p app.apks universal.apk > universal.apk
Один универсальный APK со всеми ABI и ресурсами — для раздачи вне Play и для ручного тестирования.
bundletool get-size total --apks=app.apks
bundletool get-size total --apks=app.apks --dimensions=SDK,ABI
Оценка размера загрузки для пользователя. Именно эту цифру видно в Play, а не размер AAB.
bundletool extract-apks --apks=app.apks --output-dir=out \
--device-spec=device.json
bundletool get-device-spec --output=device.json
Спецификация устройства и извлечение подходящих сплитов.
#Сборка в CI
#!/usr/bin/env bash
set -euo pipefail
export ANDROID_HOME="${ANDROID_HOME:-$HOME/Android/Sdk}"
export PATH="$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools"
export GRADLE_OPTS="-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false"
yes | sdkmanager --licenses > /dev/null
./gradlew --no-daemon --console=plain \
:app:testDebugUnitTest \
:app:assembleDebug \
:app:assembleDebugAndroidTest
Каркас шага сборки. --no-daemon в CI экономит память и убирает залипшие демоны между джобами.
# отчёты, которые стоит забирать артефактами
app/build/reports/tests/ # unit-тесты, HTML
app/build/reports/androidTests/connected/ # инструментальные
app/build/outputs/apk/debug/ # сам apk
app/build/outputs/logs/ # логи AGP
Без сохранённых отчётов разбор упавшего прогона превращается в гадание.
./gradlew lint
./gradlew :app:lintDebug
# отчёт: app/build/reports/lint-results-debug.html
Android Lint. В CI полезно ставить abortOnError и белый список, иначе предупреждения копятся годами.
| Симптом | Причина |
|---|---|
Failed to install the following SDK components | не приняты лицензии, нужен sdkmanager --licenses |
Minimum supported Gradle version is 8.11 | старый wrapper, править gradle-wrapper.properties |
No matching client found for package name | applicationId не совпадает с тем, что в google-services.json |
INSTALL_FAILED_UPDATE_INCOMPATIBLE | другая подпись — снести приложение перед установкой |
| AAB не ставится на устройство | нужен bundletool, напрямую AAB не устанавливается |
| Сборка падает только в CI | разный JDK; зафиксировать jvmToolchain |
| Эмулятор не стартует в CI | нет KVM или нужен -gpu swiftshader_indirect |