Оптимизация памяти имеет решающее значение для обеспечения плавной работы, предотвращения сбоев приложений и поддержания стабильности системы и работоспособности платформы. Хотя использование памяти должно контролироваться и оптимизироваться в каждом приложении, контентные приложения для ТВ- устройств имеют определенные проблемы, которые отличаются от типичных приложений Android для портативных устройств.
Высокое потребление памяти может привести к проблемам в работе приложений и системы, включая:
- Само приложение может начать работать медленно или зависать, а в худшем случае и вовсе прекратить работу.
- Видимые пользователю системные службы (регулировка громкости, панель настроек изображения, голосовой помощник и т. д.) начинают работать с большой задержкой или могут вообще не работать.
- Процесс-демон LMK (Low Memory Killer) может реагировать на высокую нагрузку на память, завершая наименее важные процессы; затем эти компоненты могут вскоре перезапуститься, вызывая всплески дальнейшей конкуренции за ресурсы, которые могут напрямую повлиять на приложение переднего плана.
- Переход к Launcher может быть значительно задержан, и приложение переднего плана может оставаться неотзывчивым до завершения перехода.
- Система может начать использовать direct reclaim , временно приостанавливая выполнение потоков в ожидании выделения памяти. Это может произойти с любым потоком, например, с основным потоком или потоками, связанными с кодеком, что может привести к потере аудио- и видеокадров, а также к сбоям в работе пользовательского интерфейса.
Рекомендации по выбору памяти на ТВ-устройствах
Телевизионные устройства обычно имеют значительно меньше памяти, чем телефоны или планшеты. Например, конфигурация, которую мы видим на телевизоре, — это 1 ГБ оперативной памяти и разрешение видео 1080p . В то же время большинство телевизионных приложений имеют схожие функции; следовательно, схожую реализацию и общие проблемы. Эти две ситуации представляют проблемы, которые не наблюдаются в других типах устройств и приложениях:
- Приложения Media TV обычно состоят как из изображений в сетке, так и из полноэкранных фоновых изображений , что требует загрузки большого количества изображений в память за короткий промежуток времени.
- Телевизионные приложения воспроизводят мультимедийные потоки , для воспроизведения которых требуется выделить определенный объем памяти и обеспечить плавное воспроизведение видео и аудио, а также значительные медиа-буферы.
- Дополнительные функции мультимедиа (поиск, смена эпизодов, смена аудиодорожек и т. д.) могут привести к дополнительной нагрузке на память, если они не реализованы должным образом.
Понимание телевизионных устройств
В этом руководстве основное внимание уделяется использованию памяти приложениями и целевым показателям памяти для устройств с небольшим объемом оперативной памяти.
При выборе телевизионных устройств следует учитывать следующие характеристики:
- Память устройства : объем оперативной памяти (ОЗУ), установленной на устройстве.
- Разрешение пользовательского интерфейса устройства : разрешение, используемое устройством для отображения пользовательского интерфейса ОС и приложений; обычно оно ниже разрешения видео устройства.
- Разрешение видео : максимальное разрешение, с которым устройство может воспроизводить видео.
Это приводит к классификации различных типов устройств и того, как они должны использовать память.
Обзор телевизионных устройств
Память устройства | Разрешение видео устройства | Разрешение пользовательского интерфейса устройства | isLowRAMDevice() |
---|---|---|---|
1 ГБ | 1080p | 720p | Да |
1,5 ГБ | 2160p | 1080p | Да |
≥1,5 ГБ | 1080p | 720p или 1080p | Нет* |
≥2 ГБ | 2160p | 1080p | Нет* |
Телевизионные устройства с малым объемом оперативной памяти
Эти устройства находятся в ситуации с ограниченным объемом памяти и сообщат ActivityManager.isLowRAMDevice()
как true. Приложения, работающие на ТВ-устройствах с малым объемом ОЗУ, должны реализовать дополнительные меры контроля памяти .
Мы считаем, что к этой категории относятся устройства со следующими характеристиками:
- Устройства 1 ГБ : 1 ГБ ОЗУ, разрешение пользовательского интерфейса 720p/HD (1280x720), разрешение видео 1080p/FullHD (1920x1080)
- Устройства 1,5 ГБ : 1,5 ГБ ОЗУ, разрешение пользовательского интерфейса 1080p/FullHD (1920x1080), разрешение видео 2160p/UltraHD/4K (3840x2160)
- Другие ситуации, в которых OEM-производитель определил флаг
ActivityManager.isLowRAMDevice()
из-за дополнительных ограничений памяти.
Обычные телевизионные устройства
Эти устройства не испытывают столь существенного давления памяти. Мы считаем, что эти устройства имеют следующие характеристики:
- ≥1,5 ГБ ОЗУ, пользовательский интерфейс 720p или 1080p и разрешение видео 1080p
- ≥2 ГБ ОЗУ, пользовательский интерфейс 1080p и разрешение видео 1080p или 2160p
Это не значит, что приложениям не следует беспокоиться об использовании памяти на этих устройствах, поскольку некоторые конкретные случаи неправильного использования памяти могут по-прежнему исчерпывать доступную память и приводить к снижению производительности.
Целевые значения памяти на ТВ-устройствах с малым объемом оперативной памяти
При измерении памяти на этих устройствах мы настоятельно рекомендуем контролировать каждый раздел памяти с помощью профилировщика памяти Android Studio . Приложения для ТВ должны профилировать использование памяти и работать над тем, чтобы их категории были ниже пороговых значений, которые мы определяем в этом разделе.
В разделе Как подсчитывается память вы найдете подробное объяснение сообщаемых показателей памяти. Для определения пороговых значений для ТВ-приложений мы сосредоточимся на трех категориях памяти:
- Анонимный + Подкачка : Состоит из Java + Собственный + Стек выделения памяти в Android Studio.
- Графика : напрямую сообщается в инструменте профилирования. Обычно состоит из графических текстур.
- Файл : Сообщается как категории «Код» + «Другое» в Android Studio.
С учетом этих определений в следующей таблице указано максимальное значение, которое должна использовать каждая группа памяти:
Тип памяти | Цель | Цели использования (1 ГБ) |
---|---|---|
Анонимный + Обмен (Java + Собственный + Стек) | Используется для распределений, буферов мультимедиа, переменных и других задач, требующих большого объема памяти. | < 160 МБ |
Графика | Используется графическим процессором для текстур и буферов, связанных с отображением. | 30-40 МБ |
Файл | Используется для кодовых страниц и файлов в памяти. | 60-80 МБ |
Максимальный общий объем памяти (Anon+Swap + Graphics + File) не должен превышать следующее:
- 280 МБ общего использования памяти ( Anon+Swap + Graphics + File ) для устройств с малым объемом оперативной памяти 1 ГБ.
Настоятельно рекомендуется не превышать:
- Использование памяти 200 МБ ( Anon+Swap + Graphics ).
Файловая память
В качестве общего руководства для файловой памяти следует учитывать следующее:
- В целом файловая память хорошо обрабатывается системой управления памятью ОС.
- На данный момент мы не обнаружили, что это является основной причиной нехватки памяти.
Однако при работе с файловой памятью в целом:
- Не включайте в сборку неиспользуемые библиотеки и по возможности используйте небольшие подмножества библиотек, а не полные версии.
- Не держите большие файлы открытыми в памяти и освобождайте их сразу после завершения работы с ними.
- Минимизируйте размер скомпилированного кода для классов Java и Kotlin, см. руководство «Сжатие, обфускация и оптимизация приложения» .
Конкретные рекомендации по ТВ
В этом разделе приведены конкретные рекомендации по оптимизации использования памяти на телевизионных устройствах.
Графическая память
Используйте соответствующие форматы изображений и разрешения.
- Не загружайте изображения с более высоким разрешением, чем разрешение пользовательского интерфейса устройства. Например, изображения 1080p следует уменьшить до 720p на устройстве с пользовательским интерфейсом 720p.
- По возможности используйте аппаратно-поддерживаемые растровые изображения .
- В библиотеках типа Glide включите функцию
Downsampler.ALLOW_HARDWARE_CONFIG
, которая по умолчанию отключена. Включение этой функции позволяет избежать дублирования битовых карт, которые в противном случае находились бы как в графической памяти, так и в анонимной памяти.
- В библиотеках типа Glide включите функцию
- Избегайте промежуточных рендеров и повторных рендеров
- Их можно определить с помощью Android GPU Inspector :
- В разделе «Текстуры» вы найдете изображения, которые представляют собой шаги к финальному рендеру, а не просто элементы, его формирующие. Обычно это так называемый «промежуточный рендер».
- Для приложений Android SDK вы часто можете удалить их, используя флаг макета
forceHasOverlappedRendering:false
, чтобы отключить промежуточные рендеры для этого макета. - Ознакомьтесь с полезным ресурсом «Избегайте перекрывающихся рендеров» по перекрывающимся рендерам.
- По возможности избегайте загрузки изображений-заполнителей , используйте
@android:color/
или@color
для текстур-заполнителей. - Избегайте компоновки нескольких изображений на устройстве, когда композиция может быть выполнена офлайн. Предпочитайте загружать отдельные изображения, а не делать компоновку изображений из загруженных изображений
- Чтобы лучше работать с растровыми изображениями, следуйте руководству «Обработка растровых изображений» .
Аноним+Обмен памятью
Anon+Swap состоит из выделений Native + Java + Stack в профилировщике памяти Android Studio. Используйте ActivityManager.isLowMemoryDevice()
, чтобы проверить, ограничено ли устройство памятью, и адаптируйтесь к этой ситуации, следуя этим рекомендациям.
- СМИ:
- Укажите переменный размер для медиа-буферов в зависимости от оперативной памяти устройства и разрешения воспроизведения видео . Это должно учитывать 1 минуту воспроизведения видео:
- 40-60 МБ для 1 ГБ / 1080p
- 60-80 МБ для 1,5 ГБ / 1080p
- 80-100 МБ для 1,5 ГБ / 2160p
- 100-120 МБ для 2 ГБ / 2160p
- Освобождайте выделенную медиапамять при смене эпизода, чтобы предотвратить увеличение общего объема анонимной памяти.
- Освобождайте и останавливайте медиаресурсы немедленно , когда ваше приложение останавливается: используйте обратные вызовы жизненного цикла активности для обработки аудио- и видеоресурсов. Если вы не являетесь аудиоприложением, останавливайте воспроизведение, когда в ваших активностях происходит
onStop()
, сохраняйте всю выполняемую вами работу и настройте освобождение ресурсов. Чтобы запланировать работу, которая может понадобиться позже. См. раздел Задания и сигналы тревоги .- Вы можете использовать компоненты, поддерживающие жизненный цикл, такие как
LiveData
иLifecycleOwner
, которые помогут вам обрабатывать вызовы жизненного цикла Activity. - Чтобы сделать вашу работу совместимой с жизненным циклом, вы также можете использовать сопрограммы Kotlin и потоки Kotlin .
- Вы можете использовать компоненты, поддерживающие жизненный цикл, такие как
- Обратите внимание на память буфера при поиске видео : разработчики часто выделяют дополнительные 15-60 секунд будущего контента при поиске видео, готового для пользователя, но это создает дополнительные накладные расходы памяти. В общем, не выделяйте более 5 секунд будущего буфера, пока пользователь не выберет новую позицию видео. Если вам строго необходимо предварительно буферизовать дополнительное время при поиске, убедитесь, что:
- Выделите буфер поиска заранее и используйте его повторно.
- Размер буфера не должен превышать 15-25 МБ (в зависимости от объема памяти устройства).
- Укажите переменный размер для медиа-буферов в зависимости от оперативной памяти устройства и разрешения воспроизведения видео . Это должно учитывать 1 минуту воспроизведения видео:
- Распределения:
- Используйте руководство по графической памяти, чтобы убедиться, что вы не дублируете изображения в анонимной памяти.
- Изображения часто являются крупнейшим пользователем памяти, поэтому их дублирование может оказать большую нагрузку на устройство. Это особенно актуально при интенсивной навигации по сеткам изображений.
- Освободите выделенную память, удалив ссылки на них при перемещении экранов : убедитесь, что не осталось ссылок на растровые изображения и объекты.
- Используйте руководство по графической памяти, чтобы убедиться, что вы не дублируете изображения в анонимной памяти.
- Библиотеки:
- Профилируйте выделение памяти из библиотек при добавлении новых, так как они также могут загружать дополнительные библиотеки, которые также могут выполнять выделение памяти и создавать привязки .
- Нетворкинг:
- Не выполняйте блокировку сетевых вызовов во время запуска приложения , они замедляют время запуска приложения и создают дополнительную нагрузку на память при запуске, когда память особенно ограничена загрузкой приложения. Сначала показывайте загрузочный экран или заставку и выполняйте сетевые запросы после того, как пользовательский интерфейс будет на месте.
Переплеты
Привязки приводят к дополнительным накладным расходам памяти , поскольку они переносят другие приложения в память или увеличивают потребление памяти привязанным приложением (если оно уже находится в памяти) для облегчения вызова API. В результате это уменьшает доступную память для приложения переднего плана. При привязке службы помните о том, когда и как долго вы используете привязку. Обязательно отменяйте привязку, как только она становится ненужной.
Типичные привязки и лучшие практики:
- API целостности воспроизведения : используется для проверки целостности устройства.
- Проверьте целостность устройства после загрузочного экрана и перед воспроизведением мультимедиа
- Перед воспроизведением контента освободите ссылки на PlayIntegrity
StandardIntegrityManager
.
- Библиотека Play Billing : используется для управления подписками и покупками с помощью Google Play.
- Инициализируйте библиотеку после загрузочного экрана и выполните всю работу по выставлению счетов перед воспроизведением любого медиафайла.
- Используйте
BillingClient.endConnection()
после завершения использования библиотеки и всегда перед воспроизведением видео или мультимедиа. - Используйте
BillingClient.isReady()
иBillingClient.getConnectionState()
чтобы проверить, была ли отключена служба, на случай, если потребуется повторно выполнить какие-либо действия по выставлению счетов, а затем снова выполнитеBillingClient.endConnection()
после завершения.
- Поставщик шрифтов GMS
- На устройствах с небольшим объемом оперативной памяти предпочтительнее использовать автономные шрифты, а не использовать поставщика шрифтов, поскольку загрузка шрифтов обходится дорого, а FontsProvider будет связывать службы с этой задачей.
- Библиотека Google Assistant : Иногда используется для поиска и поиска в приложении. Если возможно, замените эту библиотеку.
- Для приложений Leanback : используйте функцию преобразования текста в речь Gboard или библиотеку androidx.leanback .
- Следуйте рекомендациям по поиску для его реализации.
- Примечание: функция Leanback устарела , и приложения следует перенести в TV Compose.
- Для приложений Compose :
- Используйте функцию преобразования текста в речь Gboard для реализации голосового поиска.
- Реализуйте функцию Watch Next , чтобы сделать медиаконтент в вашем приложении легко обнаруживаемым.
- Для приложений Leanback : используйте функцию преобразования текста в речь Gboard или библиотеку androidx.leanback .
Службы переднего плана
Foreground Services — это особый тип сервиса, привязанный к уведомлению. Это уведомление отображается в области уведомлений на телефонах и планшетах, но у ТВ-устройств нет области уведомлений в том же смысле, что и у этих устройств. Даже если Foreground Services полезны, поскольку их можно поддерживать в рабочем состоянии, пока приложение находится в фоновом режиме, ТВ-приложения должны следовать следующим рекомендациям:
В Android TV и Google TV приоритетным службам разрешено продолжать работу только после того, как пользователь покидает приложение:
- Для аудиоприложений : Foreground Services разрешено продолжать работу только после того, как пользователь покидает приложение, чтобы продолжить воспроизведение аудиодорожки. Служба должна быть остановлена немедленно после завершения воспроизведения аудио.
- Для любого другого приложения: все службы переднего плана должны быть остановлены , как только пользователь покидает приложение , поскольку нет уведомления, уведомляющего пользователя о том, что приложение все еще работает и потребляет ресурсы.
- Для фоновых задач , таких как обновление рекомендаций или «Смотреть далее» , используйте
WorkManager
.
Задания и тревоги
WorkManager
— это современный API Android для планирования фоновых повторяющихся заданий. WorkManager будет использовать новый JobScheduler
, если он доступен (SDK 23+), и старый AlarmManager
, если он недоступен. Для наилучшей практики выполнения запланированных заданий на ТВ следуйте этим рекомендациям:
- Избегайте использования API
AlarmManager
в SDK 23+, особенноAlarmManager.set()
,AlarmManager.setExact()
и подобных методов, поскольку они не позволяют системе определять правильное время для запуска заданий (например, когда устройство находится в режиме ожидания). - На устройствах с малым объемом оперативной памяти избегайте запуска заданий, если это не является строго необходимым. При необходимости используйте WorkManager
WorkRequest
только для обновления рекомендаций после воспроизведения и старайтесь делать это, пока приложение все еще открыто. Определите
Constraints
WorkManager, чтобы система могла выполнять ваши задания в подходящее время:
Котлин
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresStorageNotLow(true)
.setRequiresDeviceIdle(true)
.build()
Ява
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresStorageNotLow(true)
.setRequiresDeviceIdle(true)
.build()
- Если вам необходимо регулярно запускать задания (например, для обновления Watch Next на основе активности пользователя при просмотре контента в вашем приложении на другом устройстве), то сократите использование памяти, удерживая потребление памяти заданием ниже 30 МБ .
Общие соображения относительно памяти
Следующие рекомендации предоставляют общую информацию о разработке приложений для Android:
- Минимизируйте распределение объектов, оптимизируйте повторное использование объектов и оперативно удаляйте неиспользуемые объекты.
- Не храните ссылки на объекты, особенно на растровые изображения.
- Избегайте использования
System.gc()
и прямых вызовов освобождения памяти, поскольку они мешают процессу обработки памяти системой: например, в устройствах, использующих zRAM, принудительный вызовgc()
может временно увеличить использование памяти из-за сжатия и декомпрессии памяти. - Используйте
LazyList
, как показано в браузере каталогов в Compose илиRecyclerView
в ныне устаревшем наборе инструментов Leanback UI, чтобы повторно использовать представления и не создавать элементы списка заново. - Кэшируйте локально элементы, считываемые от внешних поставщиков контента, которые вряд ли изменятся, и определяйте интервалы обновления, которые не позволяют выделять дополнительную внешнюю память.
- Проверьте наличие возможных утечек памяти.
- Обратите внимание на типичные случаи утечки памяти , такие как ссылки внутри анонимных потоков, перераспределение видеобуферов, которые никогда не освобождаются, и другие подобные ситуации.
- Используйте дамп кучи для отладки утечек памяти.
- Создавайте базовые профили , чтобы минимизировать объем оперативной компиляции, необходимой при запуске приложения с нуля.
Понимание прямого восстановления памяти
Когда приложение Android TV запрашивает память и система находится под нагрузкой, ядру Linux, лежащему в основе Android, может потребоваться использовать прямое освобождение памяти .
Процесс включает в себя полную остановку любого потока выделения для ожидания освобождения страниц памяти. Это происходит, когда фоновый процесс восстановления не может поддерживать достаточный пул памяти проактивно.
Это может привести к заметным паузам или сбоям в работе пользователя, поскольку система приостанавливает выделение потоков до тех пор, пока не будет доступно достаточно памяти. В этом смысле выделение потоков не ограничивается вызовами кода приложения, такими как malloc()
; память должна быть выделена для страницы в кодовых страницах, например.
Обзор инструментов
- Используйте инструмент профилирования памяти Android Studio для проверки потребления памяти во время использования.
- Используйте heapdump для проверки выделений конкретных объектов и битовых карт.
- Используйте собственный профилировщик памяти для проверки выделений, отличных от Java или Kotlin.
- Используйте Android GPU Inspector для проверки распределения графических ресурсов.