Оптимизация загрузки флоу и пейволов в Android SDK

Надёжная загрузка флоу или пейвола на Android решает три задачи: быстрый рендеринг, возврат варианта для целевой аудитории и корректный фолбэк при медленной сети. Правила ниже охватывают тайминг, кэширование и паттерны резервного пейвола для достижения этих целей.

Tip

Предполагается, что Adapty.activate() и Adapty.identify() уже выполнены. См. Порядок вызовов в Android SDK.

Правила и подводные камни

Делайте такНе делайте такПочему
Запрашивайте плейсмент перед показом или прогрейте кэш с помощью preloadFlows (SDK 4.1+).Запускайте собственные параллельные вызовы getFlow при старте.Самодельный пакетный запрос блокирует главный поток и вызывает чёрный экран. preloadFlows создан именно для этого и выполняет пакет параллельно.
Вызывайте getFlow после того, как атрибуция успела отработать — например, через 1–2 секунды после activate или после срабатывания setOnProfileUpdatedListener.Вызывайте getFlow в Application.onCreate().Атрибуция ещё не пришла. Флоу разрешается по аудитории по умолчанию и незаметно обходит сегменты и персонализацию ASA.
Задайте loadTimeout и настройте резервный пейвол для каждого плейсмента.Ждите ответа getFlow бесконечно.Без таймаута пользователи с плохим соединением видят пустой экран до восстановления сети — или просто закрывают приложение.

Подробнее о параметрах fetchPolicy и loadTimeout — в разделе Получение пейволов и продуктов, о выборе подходящего плейсмента — в разделе Плейсменты.

Предзагрузка плейсментов

Info

preloadFlows и preloadFlowsForDefaultAudience доступны начиная с версии SDK 4.1.

preloadFlows кэширует JSON флоу заранее — по одному запросу на плейсмент. Дальше всё работает как обычно: getFlow — для получения флоу, getFlowConfiguration — для конфигурации его представления.

fetchPolicy определяет, из какого слоя последующий getFlow читает данные в первую очередь, но не то, может ли он обратиться к кэшу вообще:

  • ReturnCacheDataElseLoad сначала читает предзагруженную копию и обращается к сети только если кэш пуст. ReturnCacheDataIfNotExpiredElseLoad(maxAgeMillis) делает то же самое, пока копия не старше maxAgeMillis.
  • По умолчанию используется ReloadRevalidatingCacheData — сначала идёт запрос в сеть, и лишь при ошибке или таймауте используется предзагруженная копия.

Предзагрузка полезна в обоих случаях, но по-разному: при политике «сначала кэш» запрос к сети не выполняется вовсе, а при политике по умолчанию запрос остаётся, но появляется тёплая копия для отката.

Используйте его, когда вы знаете, какие плейсменты понадобятся в сессии, но ещё не хотите их показывать — например, сразу после того, как разрешатся activate и identify, для флоу за кнопкой, которую пользователь ещё не нажал.

Параметры:

  • placementIds (обязательный): плейсменты для предзагрузки. Пустые и дублирующиеся ID игнорируются.
  • loadTimeout (необязательный): таймаут, применяемый к каждому плейсменту в пакете, а не ко всему пакету целиком. По умолчанию 5 секунд; значения ниже 1 секунды автоматически повышаются до 1 секунды.

Важное поведение:

  • Коллбэк срабатывает только после того, как все плейсменты были обработаны, и сообщает об ошибках по каждому плейсменту вместе. Сбой одного плейсмента не останавливает остальные.
  • Если плейсмент превысил время ожидания, вернул серверную ошибку или упал с сетевой ошибкой, SDK переключается на резервные варианты для этого плейсмента. Остальные ошибки передаются как есть.
  • Предзагрузка только прогревает кэш. Она не возвращает контент — для отображения вы по-прежнему вызываете getFlow.

Что охватывает предзагрузка

Флоу доходит до экрана послойно. Предзагрузка охватывает первый слой — ровно так же, как это делает getFlow:

СлойЗагружаетПрогревается предзагрузкой
Flow JSON — выбранный вариант, идентификаторы продуктов и Remote ConfiggetFlowДа
Макет UI — структура, стили и текст экранаgetFlowConfigurationНет
Изображения, включая стоп-кадр вместо видеоэлементаgetFlowConfiguration, в фонеНет
ВидеофайлыСистемный плеер при отрисовке экранаНе кешируются SDK

getFlowConfiguration ожидает загрузки макета, поэтому первый запрос для данного макета требует сетевого обращения даже после предзагрузки. Затем SDK сохраняет макет в собственном дисковом кэше, который сохраняется между перезапусками приложения и читается до любого сетевого запроса — таким образом, стоимость приходится на первый запрос, а не на каждый. Как только SDK получает макет, он начинает кэшировать изображения независимо от вызова: экран не блокируется, и ни колбэка, ни ошибки о завершении этого процесса не поступает.

Узнайте, какой плейсмент завершился с ошибкой

Коллбэк получает AdaptyPreloadPlacementsError, охватывающий весь пакет, с кодом REQUEST_FAILED (2005). Чтобы увидеть отдельные ошибки, прочитайте свойство preloadErrors — это map с ключами по ID плейсмента:

Adapty.preloadFlows(listOf("onboarding", "main_paywall")) { error ->
    if (error is AdaptyPreloadPlacementsError) {
        error.preloadErrors.forEach { (placementId, placementError) ->
            // log or retry the individual placement
        }
    }
}

preloadErrors существует только в AdaptyPreloadPlacementsError, поэтому сначала убедитесь в типе — любая другая AdaptyError возникла не из-за ошибки на уровне отдельного плейсмента.

Пропуск сегментации аудитории

Чтобы прогреть кэш, не дожидаясь сегментации аудитории, используйте вариант для аудитории по умолчанию. Он не принимает loadTimeout:

Adapty.preloadFlowsForDefaultAudience(listOf("main_paywall")) { error -> }

Показывайте медиа первого экрана из бандла приложения

Флоу загружает изображения и видео из Adapty. Чтобы медиа первого экрана отображалось мгновенно, отдавайте его из бандла приложения. Это удобно, если вы уже поставляете нужные файлы с приложением — например, визуальные материалы существующего нативного онбординга.

  1. В Flow & Paywall Builder задайте пользовательский медиа ID для изображения или видео. Загруженный там файл остаётся в качестве резервного.
  2. Добавьте файл в папку res/raw или assets вашего приложения.
  3. При создании представления флоу с помощью getFlowView передайте встроенный файл для этого ID в customAssets:
// "welcome_video" is the custom media ID set in the Flow & Paywall Builder
val bundledAssets = AdaptyCustomAssets.of(
    "welcome_video" to
            AdaptyCustomVideoAsset.file(
                FileLocation.fromResId(requireContext(), R.raw.welcome),
                preview = AdaptyCustomImageAsset.file(
                    FileLocation.fromResId(requireContext(), R.drawable.welcome_poster),
                ),
                resolution = AdaptyCustomVideoAsset.Resolution(width = 1080, height = 1920),
            ),
)

val flowView = AdaptyUI.getFlowView(
    activity,
    flowConfiguration,
    products,
    eventListener,
    insets,
    bundledAssets,
)

Встроенные файлы увеличивают размер загрузки приложения, поэтому встраивайте только те медиафайлы, которые пользователь видит в первую очередь.

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

Полное описание customAssets см. в разделе Настройка ресурсов.

Настройка для нестабильного соединения

Для рынков с постоянно слабым интернетом (сельские районы, транспорт, регионы с проблемами маршрутизации):

  • Установите fetchPolicy в AdaptyPlacementFetchPolicy.ReturnCacheDataElseLoad для всех запросов, кроме самого первого.
  • Настройте резервный пейвол для каждого плейсмента в дашборде Adapty.
  • Установите loadTimeout в 3–5 секунд и используйте резервный пейвол при срабатывании таймаута.
  • Не блокируйте показ флоу на getProfile. Вызывайте getFlow независимо, чтобы медленная загрузка профиля не задерживала интерфейс.