Введение

«Что такое Dynamic Feature Module и зачем он нужен?» — вопрос, после которого кандидаты с опытом 1–2 года обычно замолкают. App Bundle большинство хоть раз видело в Play Console, а вот feature-модули, on-demand доставка и Play Feature Delivery — это уже тёмный лес.

Тема доставки — одна из самых недооценённых при подготовке. Код писали, экраны верстали, а вот как приложение попадает на устройство и почему Google Play требует загружать AAB, а не APK, — объяснит не каждый. При этом это гарантированные вопросы на собеседованиях в сильные компании: они быстро показывают, выпускал ли кандидат релизы или только коммитил в ветку.

Мы уже разбирали разницу между APK и AAB. Здесь пойдём глубже: как устроен App Bundle внутри, что такое feature-модули, как работает доставка по требованию (on-demand) с Play Feature Delivery и какие ограничения нужно держать в голове.

App Bundle: формат публикации

Android App Bundle (AAB) — это формат публикации, который включает весь скомпилированный код и ресурсы приложения, но откладывает генерацию и подписание APK на сторону Google Play. Вы загружаете один файл, а Play собирает из него оптимизированные APK под каждую конфигурацию устройства: только нужная архитектура процессора, только нужная плотность экрана, только нужный язык. Подробности — в обзоре App Bundle.

Именно поэтому публикация через AAB стала обязательной: с августа 2021 года новые приложения в Google Play должны публиковаться этим форматом, а с июня 2023 года это требование распространилось и на TV-приложения.

Зачем Play это делает

Пользователь скачивает только то, что реально нужно его устройству, — меньше трафик, быстрее установка, выше конверсия. Разработчику больше не нужно собирать, подписывать и поддерживать десятки APK под разные устройства — всё делает Google Play.

Плюс у AAB есть две «надстройки» доставки: Play Feature Delivery (модули функций) и Play Asset Delivery (большие игровые ассеты). Новые приложения крупнее 200 МБ могут публиковаться именно через них.

Важно помнить и про ограничения формата:

  • Суммарный размер сжатых APK при установке (базовый APK + конфигурационные APK) — не более 4 ГБ. Повторные загрузки on-demand модулей должны укладываться в тот же лимит.
  • Формат AAB не поддерживает файлы расширений .obb — их место заняли Play Asset Delivery и feature-модули.
  • Для частичного «сайдлоада» APK без Play (когда установлены не все split-APK) возможны сбои — Play гарантирует, что при установке через магазин ставятся все нужные компоненты.

Dynamic Feature Module: что это

Dynamic Feature Module — это отдельный модуль (подпроект Gradle), который содержит код и ресурсы какой-то функции приложения и не входит в базовый модуль. Его можно доставлять по условию или скачивать в рантайме.

Типичный пример из документации Play Feature Delivery: маркетплейс, где модуль «Продажа товара» с камерой и описанием нужен только 20% пользователей. Выносим его в feature-модуль — и большинство пользователей не качают этот код при установке вовсе.

Технически это отдельный Gradle-модуль с плагином com.android.dynamic-feature, который объявлен в базовом модуле:

kotlin
app/build.gradle.kts
plugins {
    id("com.android.application")
}

android {
    namespace = "com.example.myapp"

    // Модули, которые расширяют базовый
    dynamicFeatures += listOf(":feature_payment")
}
kotlin
feature_payment/build.gradle.kts
plugins {
    id("com.android.dynamic-feature")
}

android {
    namespace = "com.example.myapp.feature.payment"
}

dependencies {
    // feature-модуль всегда зависит от базового
    implementation(project(":app"))
}

В feature-модуле не нужно дублировать то, что он наследует от базового: версию приложения (versionCode, versionName), конфигурации подписи, minifyEnabled. Всё это задаётся в базовом модуле. Если продублировать — получите конфликты сборки, о которых документация прямо предупреждает.

Способы доставки feature-модулей делятся на четыре типа:

  • Install-time — модуль ставится вместе с приложением (поведение по умолчанию). Может быть помечен как removable — тогда после выполнения функции его можно удалить с устройства.
  • Conditional — модуль попадает в установку только при выполнении условий: определённое устройство, локаль, минимальная версия API.
  • On-demand — модуль не ставится при установке, а скачивается по запросу в рантайме.
  • Instant — для Google Play Instant: пользователь «пробует» функцию без установки приложения.

On-demand доставка и Play Feature Delivery

Самый интересный для собеседования вариант — on-demand. Скачивание происходит через библиотеку Play Feature Delivery (раньше — Play Core). Классическая схема выглядит так:

  1. Создаём SplitInstallManager через фабрику.
  2. Строим запрос SplitInstallRequest и указываем модуль по имени (то же, что в манифесте как split).
  3. Запускаем установку startInstall(request) и следим за состоянием через listener.
kotlin
PaymentFeature.kt
class PaymentFeature(private val context: Context) {

    private val manager = SplitInstallManagerFactory.create(context)

    fun downloadModule(onSuccess: () -> Unit) {
        val request = SplitInstallRequest.newBuilder()
            .addModule("payment")
            .build()

        val listener = SplitInstallStateUpdatedListener { state ->
            if (state.status() == SplitInstallSessionStatus.INSTALLED) {
                onSuccess()
                manager.unregisterListener(listener)
            }
        }
        manager.registerListener(listener)
        manager.startInstall(request)
    }
}

Есть и вспомогательные операции: deferredInstall (скачать, когда приложение уйдёт в фон), installedModules (какие модули уже установлены), deferredUninstall (удалить неиспользуемый модуль).

Что важно знать про on-demand

Скачивание модуля по требованию поддерживается на устройствах с Android 5.0 (API 21) и выше. Для старых версий модуль нужно «вплавлять» в базовый — это называется fusing. И ещё одно правило: без SplitCompat приложение не получит доступ к коду и ресурсам скачанного модуля — его подключают в Application.attachBaseContext().

kotlin
MyApplication.kt
class MyApplication : Application() {

    override fun attachBaseContext(base: Context) {
        super.attachBaseContext(base)
        SplitCompat.install(this)
    }
}

Сценарий, который стоит рассказать на собеседовании: пользователь впервые заходит в раздел «Оплата» → приложение проверяет, установлен ли модуль (manager.installedModules), если нет — показывает диалог и запрашивает загрузку → после статуса INSTALLED запускает Activity из модуля. Если скачивание упало (нет сети, нет места) — модуль просто недоступен, и приложение должно это пережить без краша.

Ограничения и подводные камни

Feature-модули дают гибкость, но за неё приходится платить дисциплиной. Вот ограничения, которые я спрашиваю на собеседованиях, — и о которых почти никто не знает:

  • Не увлекайтесь количеством модулей. Установка 50 и более feature-модулей на одно устройство может привести к проблемам с производительностью. А если настроить больше 10 removable-модулей на install-time доставку — вырастет время установки.
  • Не экспортируйте компоненты из модулей. У Activity в feature-модуле не должно быть android:exported="true": другое приложение может попробовать запустить её до того, как модуль скачан, — и получить краш. Проверяйте, что модуль установлен, прежде чем обращаться к его коду и ресурсам.
  • Код между модулями не вызывается напрямую. Класс из одного модуля нельзя статически использовать в другом. Правильный паттерн — интерфейс в базовом модуле, реализация в feature-модуле, а доступ через отражение.
  • Тестирование on-demand. Чтобы проверить сценарий без Play, используйте bundletool --local-testing и FakeSplitInstallManager — он эмулирует запросы/скачивания в локальных тестах.

Самое частое — забывают SplitCompat. Модуль «скачался», статус INSTALLED, а приложение всё равно падает при обращении к классу из модуля: SplitCompat не подключён, и классы просто не видны. На собеседовании эта деталь мгновенно отделяет тех, кто реально работал с feature-модулями, от тех, кто только читал.

Вопросы на собеседовании

Соберу, что реально спрашивают и как отвечать.

  • AAB — формат публикации: Play генерирует и подписывает оптимизированные APK под каждое устройство; с августа 2021 обязателен для новых приложений.
  • Feature-модуль — подпроект с плагином com.android.dynamic-feature, объявленный в базовом модуле через dynamicFeatures.
  • Доставка — install-time, conditional, on-demand, instant; on-demand требует Android 5.0+ и SplitCompat.
  • Play Feature DeliverySplitInstallManager, SplitInstallRequest, startInstall(), listener со статусами.
  • Ограничения — 4 ГБ сжатого размера, до 10 removable-модулей, без .obb, без android:exported="true" в feature-модулях.

И типичный промах, который я слышу постоянно:

«App Bundle нужен, чтобы приложение занимало меньше места на диске у разработчика». Нет. AAB — это формат публикации; на диске у пользователя всё равно лежат собранные Play APK. Меньше становится то, что пользователь скачивает при установке, — за счёт того, что Play отдаёт только нужные под устройство split-APK.

Если спросят «зачем вообще нужен feature-модуль, если можно модуляризировать Gradle-проект обычными модулями» — отвечайте через доставку: обычные модули компилируются в один APK, а feature-модуль даёт независимую доставку и экономию размера установки.

Меня зовут Рустем Бикбулатов, я senior Android-разработчик и ментор Яндекс Практикума, провожу технические собеседования. За время менторства у меня больше 100 учеников и разборов. Сборка, Gradle и доставка — стабильно входят в список «слабых мест», которые мешают кандидатам с опытом 1–2 года перейти на уровень middle.

Хотите закрыть пробелы в сборке и публикации?

App Bundle, Dynamic Feature и релизный цикл — это не «скучная тема для DevOps», а вопросы, которые отделяют middle от junior. Бесплатная диагностика: разберу ваши пробелы и составлю план роста за 30–40 минут, без обязательств.

Итоги

App Bundle и Dynamic Feature — это про доставку, а не про сборку. Вы публикуете один AAB, Google Play собирает оптимизированные APK под каждое устройство, а feature-модули позволяют не включать часть кода в установку вовсе.

  • AAB — формат публикации, из которого Play генерирует и подписывает APK под конфигурацию устройства.
  • Feature-модуль — подпроект с com.android.dynamic-feature, объявленный в базовом модуле; доставка бывает install-time, conditional, on-demand и instant.
  • On-demandSplitInstallManager + SplitInstallRequest, требует Android 5.0+ и SplitCompat для доступа к коду модуля.

Что запомнить

Одна фраза для собеседования: «App Bundle откладывает генерацию APK на Google Play, а feature-модули позволяют доставлять часть кода по требованию через Play Feature Delivery — с проверкой установки модуля перед обращением к нему». Добавьте пример с SplitInstallManager и упоминание SplitCompat — и вопрос закрыт.

Если вы писали код, но релизы всегда выпускал кто-то другой, — самое время закрыть этот пробел: на собеседовании он «стоит» заметно больше, чем кажется. Напишите в Telegram, разберём вашу ситуацию и составим план подготовки.