Инструкции

Более детальное рассмотрение вопросов производительности

8 минут чтения
3 автора
Ben Weiss, Breana Tate, Jossi Wolf

Успокойтесь, и мы поможем вам разобраться в тонкостях исполнительского искусства.

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

Оптимизация на основе профиля

Базовые профили и профили запуска являются основополагающими для повышения производительности запуска и работы Android-приложения. Они входят в группу оптимизаций производительности, называемых оптимизацией на основе профилей.

При упаковке приложения d8 dexer берет классы и методы и заполняет файлы classes.dex вашего приложения. Когда пользователь открывает приложение, эти файлы dex загружаются один за другим, пока приложение не сможет запуститься. Предоставив профиль запуска, вы сообщаете d8, какие классы и методы следует упаковать в первые файлы classes.dex . Такая структура позволяет приложению загружать меньше файлов, что, в свою очередь, повышает скорость запуска.

Профили базовой конфигурации эффективно переносят этапы компиляции «на лету» (JIT) с пользовательских устройств на машины разработчиков. Сгенерированный код, скомпилированный «заранее» (AOT), доказал свою эффективность в сокращении времени запуска и решении проблем с рендерингом.

Профили Trello и базовые профили

Мы спросили инженеров, работающих над приложением Trello, как базовые профили повлияли на производительность их приложения. После применения базовых профилей к основному пользовательскому интерфейсу Trello отметило значительное сокращение времени запуска приложения на 25%.

image.png

Благодаря использованию базовых профилей, Trello удалось сократить время запуска своего приложения на 25%.

Базовые профили в Meta

Кроме того, инженеры компании Meta недавно опубликовали статью о том, как они ускоряют работу своих Android-приложений с помощью базовых профилей .

image.png

В приложениях Meta команды отметили улучшение различных важных показателей до 40 % после применения базовых профилей.

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

Начните работу с базовыми профилями.

Для создания базового или стартового профиля необходимо написать макротест , который проверяет работоспособность приложения. В ходе тестирования собираются данные профиля, которые будут использоваться при компиляции приложения. Тесты пишутся с использованием нового API UiAutomator , о котором мы поговорим завтра.

Создать подобный бенчмарк довольно просто, и полный пример можно посмотреть на GitHub .

@Test

fun profileGenerator() {

    rule.collect(

        packageName = TARGET_PACKAGE,

        maxIterations = 15,

        stableIterations = 3,

        includeInStartupProfile = true

    ) {

        uiAutomator {

            startApp(TARGET_PACKAGE)

        }

    }


}

Соображения

Начните с написания базового профиля и профиля запуска для макробенчмарк-тестов, описывающих наиболее часто используемый пользователями путь. Это означает основную точку входа пользователей в приложение, которая обычно находится после авторизации . Затем продолжайте писать больше тестовых случаев, чтобы получить более полную картину только для базовых профилей. Вам не нужно охватывать все с помощью базового профиля. Придерживайтесь наиболее часто используемых путей и измеряйте производительность в реальных условиях. Подробнее об этом в завтрашней публикации.

Начните работу с оптимизацией на основе профиля.

Чтобы узнать, как работают базовые профили, посмотрите это видео с саммита разработчиков Android:

А еще посмотрите эпизод Android Build Time, посвященный оптимизации на основе профилирования, для более подробного ознакомления:

У нас также имеется обширная информация о базовых профилях и профилях стартапов, с которой можно ознакомиться подробнее.

Улучшения производительности Jetpack Compose

Вклад команды разработчиков в повышение производительности пользовательского интерфейса Android оправдал себя. Начиная с версии 1.9 Jetpack Compose, рывки при прокрутке снизились до 0,2 % во внутреннем тесте производительности при длительной прокрутке.

jankyFrames.png

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

Настраиваемое окно кэша

По умолчанию, в режиме отложенной компоновки (lazy layouts) элемент компонуется заранее только в направлении прокрутки, и после того, как элемент исчезает с экрана, он удаляется. Теперь вы можете настроить количество сохраняемых элементов, регулируя размер области просмотра или dp. Это помогает вашему приложению выполнять больше работы на начальном этапе, а после включения возможности приостановки компоновки между кадрами — более эффективно использовать доступное время.

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

val dpCacheWindow = LazyLayoutCacheWindow(ahead = 150.dp, behind = 100.dp)

val state = rememberLazyListState(cacheWindow = dpCacheWindow)

LazyColumn(state = state) {

    // column contents

}

Пауза в композиции

Эта функция позволяет приостанавливать создание композиций и распределять работу над ними на несколько кадров. API-интерфейсы были добавлены в версии 1.9, и теперь они используются по умолчанию в версии 1.10 в режиме отложенной предварительной загрузки макета. Наибольшую пользу вы получите при работе со сложными элементами, требующими длительного времени для создания композиции.

image.png

Дополнительные оптимизации производительности Compose

В версиях 1.9 и 1.10 Compose команда также внесла ряд оптимизаций, которые менее очевидны.

Были улучшены несколько API, использующих сопрограммы. Например, при использовании Draggable и Clickable разработчики должны заметить более быстрое время отклика и улучшенное количество выделяемых памяти.

Оптимизация отслеживания прямоугольников компоновки улучшила производительность модификаторов, таких как onVisibilityChanged() и onLayoutRectChanged() . Это ускоряет этап компоновки, даже если эти API явно не используются.

Ещё одним способом повышения производительности является использование кэшированных значений при отслеживании позиций с помощью onPlaced() .

Предварительная загрузка текста на заднем плане

Начиная с версии 1.9, Compose добавляет возможность предварительной загрузки текста в фоновом потоке. Это позволяет предварительно прогревать кэш для ускорения компоновки текста и имеет важное значение для производительности рендеринга приложения. Во время компоновки текст должен передаваться в фреймворк Android, где заполняется кэш слов. По умолчанию это выполняется в потоке пользовательского интерфейса. Перенос предварительной загрузки и заполнение кэша слов в фоновый поток может ускорить компоновку, особенно для длинных текстов. Для предварительной загрузки в фоновом потоке можно передать пользовательский исполнитель любому компоненту, использующему BasicText , передав LocalBackgroundTextMeasurementExecutor компоненту CompositionLocalProvider следующим образом.

val defaultTextMeasurementExecutor = Executors.newSingleThreadExecutor()

CompositionLocalProvider(

    LocalBackgroundTextMeasurementExecutor provides DefaultTextMeasurementExecutor

) {

    BasicText("Some text that should be measured on a background thread!")


}

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

При оценке результатов работы учитываются следующие факторы, влияющие на эффективность выполнения работы.

Фоновая работа — важная составляющая многих приложений. Для выполнения таких задач вы можете использовать такие библиотеки, как WorkManager или JobScheduler:

  • Периодическая загрузка аналитических событий.
  • Синхронизация данных между серверной частью и базой данных.
  • Обработка медиафайлов (например, изменение размера или сжатие изображений).

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

Однако WorkManager — это не универсальное решение. В Android также есть ряд оптимизированных для повышения производительности API, разработанных специально с учетом определенных распространенных сценариев использования (Core User Journeys, CUJ).

Список некоторых из них, включая обновление виджета и получение данных о местоположении в фоновом режиме, можно найти на целевой странице «Фоновые операции» .

Инструменты локальной отладки для фоновой работы: типичные сценарии

Для отладки фоновых процессов и понимания причин задержки или сбоя задачи необходимо иметь представление о том, как система планирует ваши задачи.

Чтобы помочь в этом, WorkManager предлагает несколько связанных инструментов для локальной отладки и оптимизации производительности (некоторые из них работают и для JobScheduler!). Вот несколько распространенных сценариев, с которыми вы можете столкнуться при использовании WorkManager, и объяснение инструментов, которые можно использовать для их отладки.

Отладка причин, по которым запланированные задачи не выполняются.

Scheduled work being delayed or not executing at all can be due to a number of factors, including specified constraints not being met or constraints having been imposed by the system .

Первым шагом в выяснении причин, по которым запланированная работа не выполняется, является подтверждение успешного планирования работы . После подтверждения статуса планирования необходимо определить, существуют ли какие-либо невыполненные ограничения или предварительные условия, препятствующие выполнению работы.

Для отладки подобной ситуации существует несколько инструментов.

Инспектор фоновых задач

Инспектор фоновых задач — это мощный инструмент, интегрированный непосредственно в Android Studio. Он обеспечивает визуальное представление всех задач WorkManager и связанных с ними состояний (Выполняется, Поставлен в очередь, Сбой, Успех).

Чтобы отладить причину, по которой запланированные задачи не выполняются с помощью инспектора фоновых задач, обратитесь к перечисленным статусам задач. Статус «В очереди» означает, что ваша задача была запланирована, но все еще ожидает выполнения.

Преимущества: Помимо удобного просмотра всех задач, этот инструмент особенно полезен при работе с цепочками задач. Инспектор фоновых задач предлагает графическое представление, позволяющее визуализировать, повлиял ли сбой предыдущей задачи на выполнение следующей.

image.png

Список задач в окне инспектора фоновых задач

image.png

Графическое представление инспектора фоновых задач

adb shell dumpsys jobscheduler

Эта команда возвращает список всех активных заданий JobScheduler (включая рабочие процессы WorkManager) вместе с указанными ограничениями и ограничениями, заданными системой. Она также возвращает историю заданий.

Используйте это, если хотите просмотреть запланированные задачи и связанные с ними ограничения другим способом. Для версий WorkManager, предшествующих WorkManager 2.10.0, adb shell dumpsys jobscheduler вернет список рабочих процессов с этим именем:

[package name]/androidx.work.impl.background.systemjob.SystemJobService

Если в вашем приложении несколько обработчиков событий, обновление до WorkManager 2.10.0 позволит вам видеть имена обработчиков и легко различать их:

#WorkerName#@[package name]/androidx.work.impl.background.systemjob.SystemJobService

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

Включить отладочное логирование

Вы можете включить пользовательское логирование , чтобы просматривать подробные журналы WorkManager, к которым будет прикреплено WM— .

Преимущества: Это позволяет отслеживать планирование работ, выполнение ограничений и события жизненного цикла, а также просматривать эти журналы в процессе разработки приложения.

WorkInfo.StopReason

Если вы заметили непредсказуемую производительность конкретного воркера, вы можете программно узнать причину остановки воркера при предыдущей попытке запуска с помощью WorkInfo.getStopReason .

Рекомендуется настроить приложение на отслеживание WorkInfo с помощью getWorkInfoByIdFlow, чтобы определить, не влияют ли на вашу работу фоновые ограничения, сбои, частые таймауты или даже остановка работы пользователем.

Преимущества: Вы можете использовать WorkInfo.StopReason для сбора полевых данных о производительности ваших сотрудников.

Отладка проблемы с высокой длительностью блокировки пробуждения, вызванной WorkManager, которая была выявлена ​​системой Android Vitals.

В Android Vitals есть метрика чрезмерного количества частичных блокировок пробуждения, которая указывает на блокировки пробуждения, способствующие разряду батареи. Возможно, вас удивит, что WorkManager получает блокировки пробуждения для выполнения задач , и если количество блокировок пробуждения превышает пороговое значение, установленное Google Play, это может повлиять на видимость вашего приложения. Как можно отладить причину такого большого количества блокировок пробуждения, связанных с вашей работой? Вы можете использовать следующие инструменты.

Панель мониторинга жизненно важных показателей Android

Сначала убедитесь на панели мониторинга Android Vitals "Чрезмерная блокировка пробуждения" , что длительная блокировка пробуждения вызвана WorkManager, а не будильником или другой блокировкой пробуждения. Вы можете использовать документацию " Идентификация блокировок пробуждения, созданных другими API" , чтобы понять, какие блокировки пробуждения удерживаются из-за WorkManager.

Перфетто

Perfetto — это инструмент для анализа системных трассировок. При использовании его для отладки WorkManager вы можете просмотреть раздел «Состояние устройства», чтобы узнать, когда началась ваша работа, как долго она выполнялась и как она влияет на энергопотребление.

В разделе «Состояние устройства: Задания» можно увидеть все запущенные рабочие процессы и связанные с ними блокировки пробуждения.

deviceState.png

Раздел Device State в Perfetto, отображающий выполнение процессов CleanupWorker и BlurWorker.

Ресурсы

Для получения обзора доступных методов отладки в других сценариях, с которыми вы можете столкнуться, обратитесь к странице «Отладка WorkManager» .

Чтобы на практике опробовать некоторые из этих методов и узнать больше об отладке WorkManager, ознакомьтесь с практической работой по расширенному WorkManager и тестированию .

Следующие шаги

Сегодня мы вышли за рамки простого сокращения кода и рассмотрели, как среда выполнения Android и Jetpack Compose фактически отображают ваше приложение. Будь то предварительная компиляция критически важных участков кода с помощью базовых профилей или сглаживание состояний прокрутки с помощью новых функций Compose 1.9 и 1.10, эти инструменты ориентированы на удобство использования вашего приложения. И мы подробно углубились в лучшие практики отладки фоновой работы.

Спросите Android

В пятницу мы проведём прямую трансляцию вопросов и ответов по теме производительности. Задавайте свои вопросы прямо сейчас, используя хэштег #AskAndroid, и получите ответы от экспертов.

Задача

В понедельник мы предложили вам включить R8. Сегодня же мы просим вас создать один базовый профиль для вашего приложения.

В Android Studio Otter мастер модуля «Генератор базового профиля» делает этот процесс проще, чем когда-либо. Выберите наиболее важный для пользователя сценарий — даже если это просто запуск приложения и вход в систему — и сгенерируйте профиль.

Получив его, запустите Macrobenchmark, чтобы сравнить CompilationMode.None и CompilationMode.Partial .

Делитесь результатами улучшения времени запуска вашего проекта в социальных сетях, используя хэштег #optimizationEnabled .

Смотрите завтра!

Вы уменьшили размер своего приложения с помощью R8 и оптимизировали время выполнения с помощью оптимизации на основе профилирования. Но как доказать эти достижения заинтересованным сторонам? И как выявлять регрессии до того, как они попадут в продакшн?

Присоединяйтесь к нам завтра на 4-й день: Руководство по выравниванию производительности , где мы подробно расскажем, как измерять ваш успех, начиная от полевых данных в Play Vitals и заканчивая подробным локальным отслеживанием с помощью Perfetto.

    Автор:
    Продолжить чтение