Этот документ поможет вам выявить и устранить ключевые проблемы с производительностью вашего приложения.
Ключевые проблемы производительности
Существует множество проблем, которые могут привести к низкой производительности приложения, но ниже приведены некоторые распространенные проблемы, на которые следует обратить внимание в вашем приложении:
- Задержка запуска
Задержка запуска — это количество времени, которое проходит между нажатием на значок приложения, уведомлением или другой точкой входа и отображением данных пользователя на экране.
Стремитесь к следующим целям запуска своих приложений:
Холодный запуск занимает менее 500 мс. Холодный запуск происходит, когда запускаемое приложение отсутствует в памяти системы. Это происходит, когда приложение запускается впервые после перезагрузки или после остановки процесса приложения пользователем или системой.
Напротив, тёплый запуск происходит, когда приложение уже работает в фоновом режиме. Холодный запуск требует от системы наибольшей нагрузки, поскольку ей приходится загружать все данные из хранилища и инициализировать приложение. Постарайтесь, чтобы холодный запуск занимал не более 500 мс.
Задержки P95 и P99 очень близки к медианному значению. Длительный запуск приложения негативно сказывается на пользовательском опыте. Межпроцессное взаимодействие (IPC) и ненужные операции ввода-вывода на критическом этапе запуска приложения могут привести к конфликтам блокировок и несогласованности.
- Прокрутка зависает
Термин «задержка» описывает визуальный сбой, возникающий, когда система не может вовремя формировать и выдавать кадры для отображения их на экране с требуемой частотой 60 Гц или выше. Задержка наиболее заметна при прокрутке, когда вместо плавного анимированного потока возникают задержки. Задержка возникает, когда движение останавливается на один или несколько кадров, поскольку приложению требуется больше времени для отрисовки контента, чем длительность кадра в системе.
Приложения должны поддерживать частоту обновления 90 Гц. Стандартная частота рендеринга составляет 60 Гц, но многие новые устройства работают в режиме 90 Гц во время взаимодействия с пользователем, например, при прокрутке. Некоторые устройства поддерживают даже более высокую частоту — до 120 Гц.
Чтобы узнать, какую частоту обновления использует устройство в данный момент времени, включите наложение в разделе «Параметры разработчика» > «Показать частоту обновления» в разделе «Отладка» .
- Неплавные переходы
Это заметно во время таких взаимодействий, как переключение между вкладками или загрузка нового действия. Такие переходы должны быть плавными и анимационными, без задержек и мерцания.
- Неэффективность энергопотребления
Выполнение работы снижает заряд батареи, а выполнение ненужной работы сокращает срок ее службы.
Выделение памяти, возникающее при создании новых объектов в коде, может быть источником значительных системных затрат. Это связано не только с тем, что само выделение памяти требует усилий со стороны Android Runtime (ART), но и с последующим освобождением этих объектов ( сборкой мусора ). И выделение, и сборка памяти выполняются гораздо быстрее и эффективнее, особенно для временных объектов. Хотя раньше считалось целесообразным избегать выделения объектов, мы рекомендуем делать то, что наиболее целесообразно для вашего приложения и архитектуры. Экономия на выделении памяти с риском сделать код неподдерживаемым — не лучшая практика, учитывая возможности ART.
Однако это требует усилий, поэтому имейте в виду, что это может привести к проблемам с производительностью, если вы выделяете много объектов во внутреннем цикле.
Определить проблемы
Для выявления и устранения проблем с производительностью мы рекомендовали следующий рабочий процесс:
- Определите и проверьте следующие критические пути пользователя:
- Обычные процессы запуска, в том числе из панели запуска и уведомлений.
- Экраны, на которых пользователь прокручивает данные.
- Переходы между экранами.
- Длительные процессы, такие как навигация или воспроизведение музыки.
- Проверьте, что происходит во время предыдущих потоков, используя следующие инструменты отладки:
- Perfetto : позволяет вам видеть, что происходит на всем устройстве с точными данными о времени.
- Профилировщик памяти : позволяет увидеть, какие распределения памяти происходят в куче.
- Simpleperf : показывает диаграмму, показывающую, какие функции вызывают больше всего ресурсов процессора в течение определённого периода времени. Если вы обнаружили, что что-то занимает много времени в Systrace, но не знаете почему, Simpleperf может предоставить дополнительную информацию.
Для понимания и устранения этих проблем с производительностью крайне важно вручную отлаживать отдельные тестовые прогоны. Анализ агрегированных данных не заменит предыдущие этапы. Однако, чтобы понять, что на самом деле видят пользователи, и определить возможные регрессии, важно настроить сбор метрик в автоматизированном тестировании и в полевых условиях:
- Стартапные потоки
- Метрики поля: время запуска Play Console
- Лабораторные тесты: запуск теста с Macrobenchmark
- Янк
- Полевые метрики
- Показатели Play Console: в Play Console невозможно сузить метрики до конкретного пользовательского пути. Сервис сообщает только об общих проблемах в приложении.
- Пользовательское измерение с помощью
FrameMetricsAggregator
: вы можете использоватьFrameMetricsAggregator
для записи показателей сбоев во время определенного рабочего процесса.
- Лабораторные тесты
- Прокрутка с помощью Macrobenchmark .
- Macrobenchmark собирает данные о хронометраже кадров с помощью команд
dumpsys gfxinfo
, охватывающих один путь пользователя. Это позволяет оценить вариации рывков на протяжении пути конкретного пользователя. МетрикиRenderTime
, показывающие время отрисовки кадров, важнее количества рывков для выявления регрессий или улучшений, чем количество рывков.
- Полевые метрики
Проблемы с проверкой ссылок приложений
Ссылки приложений — это глубокие ссылки, основанные на URL-адресе вашего сайта, принадлежность которых к нему подтверждена. Ниже перечислены причины, по которым проверка ссылок приложений может быть не пройдена.
- Области фильтрации намерений: добавьте
autoVerify
только в фильтры намерений для URL-адресов, на которые может реагировать ваше приложение. - Непроверенные переключения протоколов: непроверенные перенаправления на стороне сервера и поддоменов считаются угрозой безопасности и не проходят проверку. Они приводят к сбою всех ссылок
autoVerify
. Например, перенаправление ссылок с HTTP на HTTPS, например, с example.com на www.example.com, без проверки HTTPS-ссылок может привести к сбою проверки. Обязательно проверяйте ссылки приложений , добавив фильтры намерений. - Непроверяемые ссылки: добавление непроверяемых ссылок в целях тестирования может привести к тому, что система не будет проверять ссылки приложений для вашего приложения.
- Ненадежные серверы: убедитесь, что ваши серверы могут подключаться к вашим клиентским приложениям.
Настройте свое приложение для анализа производительности
Для получения точных, воспроизводимых и применимых на практике результатов тестирования приложения крайне важно правильно настроить его. Тестируйте на системе, максимально приближенной к рабочей, подавляя источники шума. В следующих разделах представлен ряд шагов, специфичных для APK и системы, которые можно предпринять для подготовки тестовой конфигурации, некоторые из которых зависят от конкретного сценария использования.
Точки трассировки
Приложения могут оснащать свой код пользовательскими событиями трассировки .
При сборе данных трассировка вносит небольшие накладные расходы, примерно 5 мкс на секцию, поэтому не применяйте её к каждому методу. Трассировка более крупных фрагментов работы длительностью более 0,1 мс может дать ценную информацию об узких местах.
АПК-соображения
Отладочные варианты могут быть полезны для устранения неполадок и создания образцов стека, но они серьёзно влияют на производительность. Устройства под управлением Android 10 (уровень API 29) и выше могут использовать profileable android:shell="true"
в своём манифесте для включения профилирования в сборках релиза.
Используйте конфигурацию сокращения кода производственного уровня. В зависимости от ресурсов, используемых вашим приложением, это может существенно повлиять на производительность. Некоторые конфигурации ProGuard удаляют точки трассировки, поэтому рассмотрите возможность удаления этих правил для конфигурации, в которой вы запускаете тесты.
Компиляция
Скомпилируйте свое приложение на устройстве до известного состояния — как правило, speed
для простоты или speed-profile
для более точного соответствия производительности производства (хотя это требует прогрева приложения и создания профилей или компиляции базовых профилей приложения).
И speed
, и speed-profile
уменьшают объём кода, интерпретируемого из dex, и, следовательно, объём фоновой JIT-компиляции, которая может вызывать значительные помехи. Только speed-profile
снижает влияние загрузки классов из dex во время выполнения.
Следующая команда компилирует приложение с использованием speed
режима:
adb shell cmd package compile -m speed -f com.example.packagename
В режиме speed
компиляции методы приложения компилируются полностью. В режиме speed-profile
методы и классы приложения компилируются в соответствии с профилем используемых путей кода, собранным во время работы приложения. Собирать профили последовательно и правильно может быть сложно, поэтому, если вы решите их использовать, убедитесь, что они собирают именно то, что вы ожидаете. Профили находятся по следующему адресу:
/data/misc/profiles/ref/[package-name]/primary.prof
Системные соображения
Для измерений низкого и высокого уровня точности откалибруйте свои устройства. Проводите A/B-сравнения на одном и том же устройстве с одной и той же версией ОС. Производительность может значительно различаться даже на устройствах одного типа.
На устройствах с root-доступом рассмотрите возможность использования скрипта lockClocks
для Microbenchmarks. Эти скрипты, помимо прочего, выполняют следующие действия:
- Установите фиксированную частоту процессоров.
- Отключите малые ядра и настройте графический процессор.
- Отключить тепловое регулирование.
Мы не рекомендуем использовать скрипт lockClocks
для тестов, ориентированных на взаимодействие с пользователем, таких как запуск приложения, тестирование DoU и тестирование на зависание, но он может быть необходим для снижения шума в тестах Microbenchmark.
Если возможно, рассмотрите возможность использования тестовой среды, такой как Macrobenchmark , которая может снизить уровень шума в ваших измерениях и предотвратить неточность измерений.
Медленный запуск приложения: ненужная активность на батуте
Активность на батуте может неоправданно увеличивать время запуска приложения, поэтому важно знать, делает ли это ваше приложение. Как показано в следующем примере трассировки, за одним activityStart
сразу следует другой activityStart
, при этом первое activity не отрисовывает никаких кадров.
Рисунок 1. След, демонстрирующий прыжки на батуте.
Это может произойти как в точке входа уведомлений, так и в точке входа обычного приложения, и зачастую это можно исправить с помощью рефакторинга. Например, если вы используете это действие для настройки перед запуском другого действия, вынесите этот код в повторно используемый компонент или библиотеку.
Ненужные распределения, приводящие к частым сборам ресурсов
Вы можете заметить, что сборка мусора (GC) в Systrace происходит чаще, чем вы ожидаете.
В следующем примере каждые 10 секунд во время длительной операции являются показателем того, что приложение может выделять ресурсы без необходимости, но это происходит постоянно с течением времени:
Рисунок 2. Траектория, показывающая пространство между событиями ГХ.
Вы также можете заметить, что при использовании профилировщика памяти подавляющее большинство выделений памяти приходится на определённый стек вызовов. Не стоит агрессивно удалять все выделения памяти, так как это может затруднить поддержку кода. Вместо этого начните с работы с наиболее уязвимыми точками выделений.
Рамки Janky
Графический конвейер относительно сложен, и могут существовать некоторые нюансы в определении того, увидит ли пользователь в конечном итоге пропущенный кадр. В некоторых случаях платформа может «спасать» кадр с помощью буферизации. Однако вы можете игнорировать большинство этих нюансов, чтобы выявить проблемные кадры с точки зрения вашего приложения.
Когда отрисовываются кадры, не требующие от приложения больших усилий, точки трассировки Choreographer.doFrame()
возникают с частотой 16,7 мс на устройстве с частотой 60 FPS:
Рисунок 3. Траектория, показывающая частые быстрые кадры.
Если уменьшить масштаб и просмотреть трассировку, то иногда можно увидеть, что кадры обрабатываются немного дольше, но это не страшно, поскольку они не превышают отведенные им 16,7 мс:
Рисунок 4. Траектория, показывающая частые быстрые кадры с периодическими всплесками работы.
Если вы видите нарушение этого регулярного ритма, то это нестабильный кадр, как показано на рисунке 5:
Рисунок 5. След, показывающий дрожание кадра.
Вы можете попрактиковаться в их распознавании.
Рисунок 6. Траектория, показывающая больше неровных кадров.
В некоторых случаях вам потребуется увеличить масштаб точки трассировки, чтобы получить более подробную информацию о том, какие представления расширяются или что делает RecyclerView
. В других случаях может потребоваться более детальное исследование.
Дополнительную информацию об определении дергающихся кадров и устранении причин их возникновения см. в разделе Медленная отрисовка .
Распространенные ошибки RecyclerView
Необоснованная аннулирование всех данных RecyclerView
может привести к увеличению времени рендеринга кадров и подтормаживанию. Вместо этого, чтобы минимизировать количество обновляемых представлений, аннулируйте только те данные, которые изменяются.
В разделе Представление динамических данных описываются способы избежания дорогостоящих вызовов notifyDatasetChanged()
, которые приводят к обновлению контента, а не к его полной замене.
Если вы не поддерживаете все вложенные RecyclerView
должным образом, это может привести к полному пересозданию внутреннего RecyclerView
каждый раз. Для каждого вложенного внутреннего RecyclerView
должен быть установлен RecycledViewPool
, чтобы обеспечить возможность повторного использования представлений между каждым внутренним RecyclerView
.
Недостаточная предварительная загрузка данных или её несвоевременная загрузка может привести к тому, что пользователь, ожидая получения данных от сервера, будет испытывать дискомфорт при достижении конца прокручиваемого списка. Хотя технически это не является проблемой, поскольку сроки загрузки всех кадров не пропускаются, вы можете значительно улучшить пользовательский опыт, изменив время и объём предварительной загрузки, чтобы пользователю не приходилось ждать данные.
Отладка вашего приложения
Ниже представлены различные методы отладки производительности приложения. В следующем видео представлен обзор трассировки системы и использования профилировщика Android Studio.
Отладка запуска приложения с помощью Systrace
Обзор процесса запуска приложения см. в разделе Время запуска приложения, а обзор трассировки системы см. в следующем видео.
Различать типы стартапов можно на следующих этапах:
- Холодный запуск: начните с создания нового процесса без сохраненного состояния .
- Теплый запуск: либо воссоздает активность при повторном использовании процесса, либо воссоздает процесс с сохраненным состоянием.
- Горячий старт: перезапускает деятельность и начинает с уровня инфляции.
Мы рекомендуем захватывать файлы трассировки Systraces с помощью приложения System Tracing на устройстве . Для Android 10 и более поздних версий используйте Perfetto . Для Android 9 и более ранних версий используйте Systrace . Мы также рекомендуем просматривать файлы трассировки с помощью веб-сервиса Perfetto Tracing Viewer . Подробнее см. в разделе Обзор трассировки системы .
Вот некоторые вещи, на которые следует обратить внимание:
- Конкуренция за монитор: конкуренция за ресурсы, защищенные монитором, может привести к значительной задержке запуска приложения.
Синхронные транзакции связующего компонента: найдите ненужные транзакции на критическом пути вашего приложения. Если необходимая транзакция требует больших затрат, рассмотрите возможность сотрудничества с командой соответствующей платформы для внесения улучшений.
Одновременная сборка мусора: это распространенная проблема и относительно небольшое влияние, но если вы сталкиваетесь с ней часто, рассмотрите возможность ее решения с помощью профилировщика памяти Android Studio.
Ввод-вывод: проверьте операции ввода-вывода, выполняемые во время запуска, и обратите внимание на длительные простои.
Значительная активность в других потоках: они могут мешать потоку пользовательского интерфейса, поэтому следите за фоновой работой во время запуска.
Мы рекомендуем вызывать reportFullyDrawn
после завершения запуска с точки зрения приложения для улучшения отчётности по метрикам запуска. Подробнее об использовании reportFullyDrawn
см. в разделе «Время до полного отображения» . Вы можете извлечь время запуска, определённое RFD, с помощью процессора трассировки Perfetto, и будет создано видимое пользователю событие трассировки.
Используйте системную трассировку на устройстве
Вы можете использовать системное приложение System Tracing для записи трассировки системы на устройстве . Это приложение позволяет записывать трассировку с устройства без необходимости его подключения к компьютеру или adb
.
Используйте профилировщик памяти Android Studio
Вы можете использовать инструмент Android Studio Memory Profiler для анализа нагрузки на память, которая может быть вызвана утечками памяти или неправильным использованием. Он предоставляет информацию о выделении объектов в режиме реального времени.
Вы можете устранить проблемы с памятью в своем приложении, используя информацию из Memory Profiler, которая позволяет отслеживать причины и частоту возникновения GC.
Чтобы профилировать память приложения, выполните следующие действия:
Выявить проблемы с памятью.
Запишите сеанс профилирования памяти для того этапа пути пользователя, на котором вы хотите сосредоточиться. Обратите внимание на увеличение количества объектов, как показано на рисунке 7, которое в конечном итоге приводит к появлению мусорных контейнеров, как показано на рисунке 8.
Рисунок 7. Увеличение количества объектов.
Рисунок 8. Сбор мусора.
Определив путь пользователя, который создает нагрузку на память, проанализируйте основные причины этой нагрузки.
Диагностируйте очаги давления в памяти.
Выберите диапазон на временной шкале для визуализации как распределения , так и поверхностного размера , как показано на рисунке 9.
Рисунок 9. Значения распределений и мелкого размера .
Существует несколько способов сортировки этих данных. Ниже приведены примеры того, как каждое представление может помочь вам в анализе проблем.
Упорядочить по классу : полезно, когда вы хотите найти классы, генерирующие объекты, которые в противном случае кэшируются или повторно используются из пула памяти.
Например, если приложение создаёт 2000 объектов класса «Vertex» каждую секунду, оно увеличивает количество выделений на 2000 каждую секунду, и это видно при сортировке по классу. Если вы хотите повторно использовать эти объекты, чтобы избежать создания мусора, реализуйте пул памяти.
Упорядочить по стеку вызовов : полезно, когда вы хотите найти горячий путь, в котором выделяется память, например, внутри цикла или внутри определенной функции, выполняющей большой объем работы по выделению памяти.
Небольшой размер : отслеживает только память самого объекта. Это полезно для отслеживания простых классов, состоящих в основном из примитивных значений.
Сохранённый размер : показывает общий объём памяти, занимаемой объектом и ссылками, на которые ссылается только объект. Это полезно для отслеживания нагрузки на память, связанной со сложными объектами. Чтобы получить это значение, сделайте полный дамп памяти, как показано на рисунке 10, и добавьте значение «Сохранённый размер» в виде столбца, как показано на рисунке 11.
Рисунок 10. Полный дамп памяти.
Рисунок 11. Столбец «Сохраненный размер».
Измерение влияния оптимизации.
GC становятся более очевидными, и влияние оптимизации памяти легче оценить. Когда оптимизация снижает нагрузку на память, количество GC уменьшается.
Чтобы оценить влияние оптимизации, на временной шкале профилировщика измерьте время между сборками мусора. Вы увидите, что интервал между сборками мусора увеличился.
Конечные результаты улучшения памяти следующие:
- Вероятность сбоев из-за нехватки памяти сократится, если приложение не будет постоянно сталкиваться с нехваткой памяти.
- Уменьшение количества сборщиков мусора улучшает показатели зависаний, особенно в P99. Это связано с тем, что сборщики мусора вызывают конфликт ресурсов процессора, что может привести к откладыванию задач рендеринга во время сборки мусора.
Рекомендовано для вас
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Анализ и оптимизация запуска приложения {:#app-startup-analysis-optimization}
- Замороженные кадры
- Написать макробенчмарк