Профили запуска и Baseline Profiles
Ускоряйте холодный запуск
«Профили запуска и Baseline Profiles» — бесплатный урок Android Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Android Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Android Academy содержит 4 уроков всего.
Первое впечатление: запуск приложения
Время запуска — первое, с чем сталкивается каждый пользователь. Google Play даже отмечает медленный запуск как проблему качества. Быстрый запуск с нуля создаёт ощущение качественного приложения, а медленный заставляет пользователей уходить.
В этом уроке Вы узнаете о трёх типах запуска, причинах медленного запуска и современных инструментах — библиотеке запуска приложения и базовых профилях, — которые значительно ускоряют запуск с нуля.
Запуск с нуля, тёплый и горячий запуск
Android определяет три сценария запуска — от самого медленного к самому быстрому:
- Запуск с нуля: процесса ещё не существует. Android создаёт его, запускает
Application, а затем первый экран. Это самый медленный сценарий и главный объект оптимизации. - Тёплый запуск: процесс работает, но Activity необходимо создать заново.
- Горячий запуск: Activity всё ещё находится в памяти; нужно лишь вывести её на передний план. Почти мгновенно.
Основные усилия по оптимизации направлены на запуск с нуля, поскольку именно с ним чаще всего сталкиваются новые и возвращающиеся пользователи.
Измерение времени запуска
Невозможно улучшить то, что Вы не измеряете. Есть два простых способа:
- Logcat: система записывает строку
Displayedсо временем до появления первого кадра. - Макротестирование производительности:
StartupTimingMetricпредоставляет стабильные и воспроизводимые значения для запуска с нуля.
Выполните приведённую ниже команду adb и найдите строку Displayed.
# Cold-start the app and log time to first frame
adb shell am start -W -S com.example.app/.MainActivity
# Output includes:
# TotalTime: 412 <- ms to first frame
# Or filter logcat:
adb logcat | grep "Displayed com.example.app"Сохраняйте лёгкость Application.onCreate
Всё, что выполняется в Application.onCreate(), запускается в главном потоке до появления первого кадра. Тяжёлая инициализация здесь напрямую замедляет запуск.
В неудачном варианте ниже несколько библиотек инициализируются заранее. Отложите или перенесите в фоновый поток всё, что не требуется для первого экрана.
// SLOW: blocks the first frame with eager init
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
Analytics.init(this) // network, disk
ImageLoader.preload(this) // heavy
Database.warmUp(this) // disk I/O
}
}
// Each of these adds milliseconds before the user sees anything.Библиотека запуска приложения Jetpack
Библиотека запуска приложения заменяет несколько поставщиков содержимого библиотек (каждый из них требует времени) одним общим поставщиком и позволяет наглядно задавать порядок и зависимости инициализации.
Для каждого компонента Вы реализуете отдельный Initializer; библиотека запуска приложения выполняет их один раз в порядке зависимостей.
class AnalyticsInitializer : Initializer<Analytics> {
override fun create(context: Context): Analytics {
return Analytics.init(context.applicationContext)
}
// Runs after Logger is ready
override fun dependencies() = listOf(LoggerInitializer::class.java)
}
// Registered via a single merged provider in the manifest,
// avoiding one ContentProvider per library.Отложенная и фоновая инициализация
Ещё лучше, чем упорядочивать предварительную работу, — сократить её объём при запуске. Вот два приёма:
- Отложенная инициализация: создавайте тяжёлые объекты при первом использовании с помощью конструкции Kotlin
by lazy. - Фоновая инициализация: переносите инициализацию, не связанную с интерфейсом, из главного потока.
Теперь первый кадр не блокируется работой, которая пока не нужна пользователю.
class MyApp : Application() {
// Built only when first accessed, not during onCreate
val imageLoader by lazy { ImageLoader.build(this) }
override fun onCreate() {
super.onCreate()
// Push non-critical setup off the main thread
CoroutineScope(Dispatchers.Default).launch {
Analytics.init(applicationContext)
}
}
}AOT и JIT: почему первый запуск медленный
По умолчанию Android выполняет байт-код приложения, сочетая интерпретацию и компиляцию «точно вовремя» (JIT). При первом выполнении часто используемый код интерпретируется (медленно), и только позже среда выполнения компилирует его в машинный код.
Именно поэтому запуск с нуля и первая прокрутка ощущаются медленнее. Базовые профили решают эту проблему: они указывают устройству скомпилировать важный код заранее (AOT) во время установки.
Что такое базовый профиль
Базовый профиль — это список классов и методов, которые используются в важных пользовательских сценариях (при запуске и первой прокрутке). Вы поставляете его вместе с приложением; во время установки устройство заранее компилирует эти методы (AOT), поэтому уже при первом запуске они выполняются с машинной скоростью.
По данным Google, ускорение запуска часто составляет 20–40 %, при этом в код функций не требуется вносить изменений — нужен только профиль.
// build.gradle.kts
plugins { id("androidx.baselineprofile") }
dependencies {
baselineProfile(project(":baselineprofile"))
}
// The generated profile ships as
// assets/dexopt/baseline.prof
// and is applied automatically at install.Создание базового профиля
Профиль создаётся с помощью небольшого теста, который выполняет критический сценарий на устройстве; инструменты записывают запущенные методы и создают файл профиля. Затем Вы добавляете его в систему контроля версий и выполняете повторную сборку.
Ниже приведён типичный генератор, который записывает запуск и прокрутку.
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {
@get:Rule val rule = BaselineProfileRule()
@Test
fun generate() = rule.collect(packageName = "com.example.app") {
pressHome()
startActivityAndWait()
// exercise the critical journey
device.findObject(By.res("feed")).fling(Direction.DOWN)
}
}
// Run the generateBaselineProfile Gradle task to produce the file.Проверка результата
Всегда подтверждайте улучшение с помощью теста производительности, сравнивая запуск с применённым профилем и без него (CompilationMode.None и Partial с профилем).
Если Вы не проводите измерения, то не сможете доказать пользу профиля, а устаревший профиль даже может ухудшить результат. Создавайте его заново, когда важные участки кода значительно изменятся.
@Test
fun startupWithProfile() = rule.measureRepeated(
packageName = "com.example.app",
metrics = listOf(StartupTimingMetric()),
iterations = 10,
startupMode = StartupMode.COLD,
compilationMode = CompilationMode.Partial() // uses the baseline profile
) {
pressHome()
startActivityAndWait()
}План оптимизации запуска
Соберите всё в повторяемый план:
- Измерьте запуск с нуля с помощью макротестирования производительности и
adb am start -W. - Сократите работу в
Application.onCreate: используйтеby lazyдля отложенной инициализации и перенесите работу из главного потока. - Используйте библиотеку запуска приложения, чтобы объединить поставщиков содержимого и упорядочить инициализацию.
- Поставляйте базовый профиль для AOT-компиляции критического сценария.
- Проверьте результат с помощью теста производительности и поддерживайте профиль актуальным.
Результат: быстрый первый кадр, который пользователи сразу замечают.
Быстрая проверка
Вы поставляете базовый профиль вместе с приложением. Что он в первую очередь делает для ускорения запуска?
Итоги: быстро с первого кадра
Вы научились оптимизировать запуск — самый заметный показатель производительности:
- Оптимизируйте запуск с нуля; измеряйте его с помощью макротестирования производительности и
adb am start -W. - Сохраняйте лёгкость
Application.onCreate: используйте отложенную инициализацию и выполняйте фоновую работу, не связанную с интерфейсом. - Используйте библиотеку запуска приложения, чтобы объединить поставщиков и упорядочить инициализаторы.
- Поставляйте базовый профиль, чтобы горячий код компилировался заранее (AOT) во время установки и выполнялся с машинной скоростью уже при первом запуске.
- Всегда проверяйте результат с помощью теста производительности и поддерживайте профиль актуальным.
На этом завершается раздел «Оптимизация и профилирование производительности»: теперь Вы умеете измерять производительность, управлять рекомпозицией, исправлять утечки и ускорять запуск.
Часто задаваемые вопросы
Урок «Профили запуска и Baseline Profiles» бесплатный?
Да — полный текст урока «Профили запуска и Baseline Profiles» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Android Academy, подпишись на CoddyKit PRO. Курс Android Academy содержит 4 уроков всего.
Чему я научусь в уроке «Профили запуска и Baseline Profiles»?
Ускоряйте холодный запуск Ты практикуешь Android Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Android Academy?
Предыдущий опыт не требуется. Android Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Профили запуска и Baseline Profiles»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Android Academy?
Да. Каждый урок Android Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Измерение производительности
- Управление рекомпозицией
- Утечки памяти и исправления
- Профили запуска и Baseline Profiles