Оптимизация памяти необходима для обеспечения стабильной работы, предотвращения сбоев приложений, а также поддержания стабильности системы и работоспособности платформы. Хотя использование памяти следует отслеживать и оптимизировать в каждом приложении, приложения для просмотра контента на телевизорах сталкиваются с особыми проблемами, которые отличаются от проблем обычных приложений Android для портативных устройств.
Высокое потребление памяти может привести к проблемам с работой приложений и системы, в том числе:
- Само приложение может работать медленно или с задержками, а в худшем случае – быть закрыто.
- Системные сервисы, видимые пользователю (регулятор громкости, панель настроек изображения, голосовой помощник и т. д.), начинают работать с задержками или перестают работать вовсе.
- Демон low memory killer (LMK) может реагировать на нехватку памяти, завершая наименее важные процессы. Затем эти компоненты могут перезапуститься, что приведет к дальнейшему конфликту ресурсов, который может напрямую повлиять на работу активного приложения.
- Переход к панели запуска может значительно задержаться, и активное приложение будет выглядеть неотвечающим, пока переход не завершится.
- Система может начать использовать прямое освобождение, временно приостанавливая выполнение потока в ожидании выделения памяти. Это может произойти с любой цепочкой, например с основной или связанной с кодеком, и привести к пропуску кадров аудио и видео, а также к сбоям в работе интерфейса.
Особенности использования памяти на телевизорах
У телевизоров обычно гораздо меньше памяти, чем у телефонов или планшетов. Например, на телевизоре может быть 1 ГБ ОЗУ и разрешение видео 1080p. При этом большинство приложений для телевизоров имеют похожие функции, а значит, и похожие проблемы с реализацией. Эти две ситуации создают проблемы, которые не встречаются в других типах устройств и приложений:
- Приложения для просмотра медиаконтента обычно состоят из изображений в виде сетки и полноэкранных фоновых изображений, для которых требуется загрузить в память большое количество изображений за короткий промежуток времени.
- Приложения для телевизоров воспроизводят мультимедийные потоки, для которых требуется определенный объем памяти, а также значительные медиабуферы, чтобы обеспечить плавное воспроизведение видео и аудио.
- Дополнительные функции медиаконтента (перемотка, смена серии, смена звуковой дорожки и т. д.) могут создавать дополнительную нагрузку на память, если они реализованы неправильно.
Телевизоры
В этом руководстве основное внимание уделяется использованию памяти приложениями и целевым показателям памяти для устройств с небольшим объемом ОЗУ.
При выборе телевизора учитывайте следующие характеристики:
- Память устройства. Объем оперативной памяти (ОЗУ), установленной на устройстве.
- Разрешение интерфейса устройства. Разрешение, которое устройство использует для отрисовки интерфейса ОС и приложений. Обычно оно ниже разрешения видео.
- Разрешение видео. Максимальное разрешение, в котором устройство может воспроизводить видео.
Это позволяет классифицировать устройства по типам и определить, как они должны использовать память.
Сводка по телевизорам
| Память устройства | Разрешение видео на устройстве | Разрешение интерфейса устройства | isLowRamDevice() |
|---|---|---|---|
| 1 ГБ | 1080p | 720p | Да |
| 1,5 ГБ | 2160p | 1080p | Да |
| ≥ 1,5 ГБ | 1080p | 720p или 1080p | Нет* |
| ≥ 2 ГБ | 2160p | 1080p | Нет* |
Телевизоры с небольшим объемом ОЗУ
Эти устройства ограничены в памяти и будут сообщать ActivityManager.isLowRamDevice() как истинное значение. Приложения, работающие на телевизорах с небольшим объемом ОЗУ, должны реализовывать дополнительные меры контроля памяти.
К этой категории относятся устройства со следующими характеристиками:
- Устройства с 1 ГБ ОЗУ: 1 ГБ ОЗУ, разрешение интерфейса 720p/HD (1280x720), разрешение видео 1080p/FullHD (1920x1080).
- Устройства с 1,5 ГБ ОЗУ: 1,5 ГБ ОЗУ, разрешение интерфейса 1080p/FullHD (1920x1080), разрешение видео 2160p/UltraHD/4K (3840x2160).
- Другие ситуации, в которых производитель устройства задал флаг
ActivityManager.isLowRamDevice()из-за дополнительных ограничений памяти.
обычные телевизоры;
На таких устройствах не возникает значительной нехватки памяти. Мы считаем, что эти устройства обладают следующими характеристиками:
- не менее 1,5 ГБ ОЗУ, интерфейс 720p или 1080p и разрешение видео 1080p;
- не менее 2 ГБ ОЗУ, интерфейс 1080p и разрешение видео 1080p или 2160p;
Это не значит, что приложениям не нужно следить за использованием памяти на таких устройствах, поскольку некоторые ошибки в работе с памятью могут привести к ее нехватке и снижению производительности.
Целевые показатели памяти на телевизорах с небольшим объемом ОЗУ
При измерении памяти на таких устройствах мы настоятельно рекомендуем отслеживать каждый раздел памяти с помощью профилировщика памяти Android Studio. Приложения для телевизоров должны профилировать использование памяти и стремиться к тому, чтобы их категории были ниже пороговых значений, которые мы определяем в этом разделе.

В разделе Как учитывается память приводится подробное объяснение того, как рассчитываются показатели памяти. Пороговые значения для приложений на ТВ будут определяться на основе трех категорий памяти:
- Анонимная память + файл подкачки. Состоит из памяти, выделенной для Java, нативных библиотек и стека в Android Studio.
- Графика. Данные, полученные непосредственно из инструмента профилирования. Обычно состоит из текстур графики.
- Файл. В Android Studio относится к категориям "Код" и "Другое".
С учетом этих определений в таблице ниже указано максимальное значение, которое должна использовать каждая группа памяти:
| Тип памяти | Purpose | Целевые показатели использования (1 ГБ) |
|---|---|---|
| Анонимная память + файл подкачки (Java + Native + Stack) | Используется для распределения памяти, буферов мультимедиа, переменных и других задач, требующих большого объема памяти. | < 160 МБ |
| Графика | Используется графическим процессором для текстур и буферов, связанных с дисплеем. | 30–40 МБ |
| Файл | Используется для страниц кода и файлов в памяти. | 60–80 МБ |
Максимальный общий объем памяти (анонимная память + файл подкачки + графическая память + память для файлов) не должен превышать следующие значения:
- 280 МБ общего использования памяти (анонимные данные об RSS и swap-пространстве + графика + файлы) для устройств с низким объемом ОЗУ (1 ГБ).
Настоятельно рекомендуется не превышать следующие значения:
- 200 МБ использования памяти (анонимный RSS + файл подкачки + графика).
Память файлов
В качестве общего руководства по использованию памяти, поддерживаемой файлами, следует учитывать следующее:
- Как правило, памятью файлов хорошо управляет ОС.
- В настоящее время мы не считаем, что это основная причина нехватки памяти.
Однако при работе с памятью файлов в целом:
- Не добавляйте в сборку неиспользуемые библиотеки. По возможности используйте не полные библиотеки, а их небольшие подмножества.
- Не держите большие файлы открытыми в памяти и закрывайте их, как только они вам больше не нужны.
- Уменьшите размер скомпилированного кода для классов Java и Kotlin. Подробнее о том, как это сделать, рассказывается в руководстве Как уменьшить размер приложения, запутать код и оптимизировать его.
Рекомендации по выбору телевизора
В этом разделе приведены рекомендации по оптимизации использования памяти на устройствах Android TV.
Память графического процессора
Используйте подходящие форматы и разрешения изображений.
- Не загружайте изображения с разрешением выше, чем разрешение интерфейса устройства. Например, если разрешение экрана устройства составляет 720p, изображения с разрешением 1080p должны быть уменьшены до 720p.
- По возможности используйте аппаратные растровые изображения.
- В библиотеках, таких как Glide, включите функцию
Downsampler.ALLOW_HARDWARE_CONFIG, которая по умолчанию отключена. Включив эту функцию, вы избежите дублирования растровых изображений, которые в противном случае будут храниться и в графической, и в анонимной памяти.
- В библиотеках, таких как Glide, включите функцию
- Избегайте промежуточных и повторных отрисовок
- Их можно выявить с помощью Android GPU Inspector:
- В разделе "Текстуры" ищите изображения, которые являются этапами финальной визуализации, а не просто элементами, из которых она состоит. Обычно это так называемая промежуточная визуализация.
- В приложениях Android SDK часто можно удалить эти элементы, используя флаг макета
forceHasOverlappedRendering:falseдля отключения промежуточных отрисовок для этого макета. - Рекомендуем ознакомиться с разделом Избегайте перекрывающихся отрисовок.
- Старайтесь не загружать замещающие изображения. Используйте
@android:color/или@colorдля замещающих текстур. - Не объединяйте несколько изображений на устройстве, если это можно сделать в офлайн-режиме. Предпочитать загрузку отдельных изображений, а не составление изображений из скачанных файлов.
- Чтобы лучше работать с растровыми изображениями, следуйте инструкциям из руководства Работа с растровыми изображениями.
Память Anon+Swap
Anon+Swap состоит из нативных, Java- и стековых выделений в профилировщике памяти Android Studio. Используйте ActivityManager.isLowMemoryDevice(), чтобы проверить, ограничена ли память устройства, и адаптируйте приложение к этой ситуации, следуя приведенным ниже рекомендациям.
- Медиа
- Укажите переменный размер буферов мультимедиа в зависимости от объема ОЗУ устройства и разрешения воспроизведения видео. Это должно соответствовать одной минуте воспроизведения видео:
- 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 секунд контента при перемотке, чтобы видео было готово для пользователя, но это создает дополнительную нагрузку на память.
В общем случае не следует использовать буфер более пяти секунд, пока пользователь не выберет новую позицию в видео. Если вам необходимо предварительно буферизировать дополнительное время при перемотке, убедитесь, что:
- Заранее выделите буфер для перемотки и используйте его повторно.
- Размер буфера не должен превышать 15–25 МБ (в зависимости от объема памяти устройства).
- Укажите переменный размер буферов мультимедиа в зависимости от объема ОЗУ устройства и разрешения воспроизведения видео. Это должно соответствовать одной минуте воспроизведения видео:
- Распределение:
- Используйте рекомендации по использованию памяти для графики, чтобы не дублировать изображения в анонимной памяти.
- Изображения часто занимают больше всего места в памяти, поэтому их дублирование может сильно нагружать устройство. Это особенно заметно при просмотре большого количества изображений.
- Освобождайте выделенную память, удаляя ссылки на нее при переходе между экранами. Убедитесь, что не осталось ссылок на растровые изображения и объекты.
- Используйте рекомендации по использованию памяти для графики, чтобы не дублировать изображения в анонимной памяти.
- Библиотеки:
- Распределение памяти профиля из библиотек при добавлении новых библиотек, поскольку они могут загружать дополнительные библиотеки, которые также могут распределять память и создавать привязки.
- Общение в неформальной обстановке
- Не блокировать сетевые вызовы при запуске приложения. Они замедляют запуск приложения и создают дополнительную нагрузку на память, которая и так ограничена при загрузке приложения. Сначала покажите экран загрузки или заставку, а затем выполняйте сетевые запросы.
Привязки
Привязки создают дополнительную нагрузку на память, поскольку переносят в память другие приложения или увеличивают потребление памяти привязанным приложением (если оно уже находится в памяти), чтобы обеспечить вызов API. В результате уменьшается объем памяти, доступный для активного приложения. Привязывая службу, учитывайте, когда и как долго вы будете использовать привязку. Обязательно отмените привязку, как только она станет ненужной.
Типичные привязки и рекомендации:
- Play Integrity API используется для проверки целостности устройства.
- Проверять целостность данных на устройстве после экрана загрузки и перед воспроизведением медиаконтента.
- Перед воспроизведением контента освободите ссылки на PlayIntegrity
StandardIntegrityManager.
- Библиотека Play Платежей используется для управления подписками и покупками через Google Play.
- Инициализируйте библиотеку после экрана загрузки и выполните все операции, связанные с оплатой, до воспроизведения медиаконтента.
- Используйте
BillingClient.endConnection()после того, как закончите работу с библиотекой, и всегда перед воспроизведением видео или медиафайлов. - Используйте
BillingClient.isReady()иBillingClient.getConnectionState(), чтобы проверить, не был ли отключен сервис, если нужно повторно выполнить какие-либо платежные операции, а затем снова выполнитеBillingClient.endConnection().
- GMS FontsProvider
- На устройствах с небольшим объемом ОЗУ лучше использовать отдельные шрифты, а не поставщика шрифтов, поскольку скачивание шрифтов требует много ресурсов, а FontsProvider будет привязывать сервисы для этого.
- Библиотека Google Ассистента. Иногда используется для поиска и поиска в приложении. Если возможно, замените эту библиотеку.
- Для приложений leanback используйте функцию преобразования текста в речь Gboard или библиотеку androidx.leanback.
- При реализации поиска следуйте рекомендациям для Google Поиска.
- Примечание. Leanback устарел, и приложения следует перенести на TV Compose.
- Для приложений, созданных с помощью Compose:
- Используйте функцию преобразования текста в речь Gboard для голосового поиска.
- Реализуйте функцию Что посмотреть дальше, чтобы пользователи могли находить медиаконтент в вашем приложении.
- Для приложений leanback используйте функцию преобразования текста в речь Gboard или библиотеку androidx.leanback.
Активные службы
Активные службы – это особый тип служб, связанных с уведомлениями. Это уведомление показывается в панели уведомлений на телефонах и планшетах, но на телевизорах такой панели нет. Активные службы могут быть полезны, поскольку они продолжают работать, когда приложение находится в фоновом режиме. Однако приложения для телевизоров должны соответствовать следующим требованиям:
В Android TV и Google TV активные службы могут работать после того, как пользователь покинет приложение, только в следующих случаях:
- Для аудиоприложений Активные службы могут продолжать работу после того, как пользователь покинет приложение, чтобы воспроизводить звуковую дорожку. Сервис должен быть остановлен сразу после окончания воспроизведения аудио.
- Для любого другого приложения: все активные службы должны быть остановлены, когда пользователь покидает приложение, поскольку нет уведомления, которое сообщало бы пользователю, что приложение по-прежнему работает и потребляет ресурсы.
- Для фоновых задач, например обновления рекомендаций или списка "Смотреть дальше", используйте
WorkManager.
Работа и будильники
WorkManager – это современный API Android для планирования фоновых повторяющихся задач.
WorkManager будет использовать новый JobScheduler, если он доступен (SDK 23 и более поздних версий), и старый AlarmManager, если нет. Чтобы запланированные задания на телевизоре выполнялись корректно, следуйте приведенным ниже рекомендациям.
- Не используйте API
AlarmManagerна устройствах с SDK 23 и более поздних версий, особенноAlarmManager.set(),AlarmManager.setExact()и похожие методы, поскольку они не позволяют системе определять подходящее время для выполнения задач (например, когда устройство не используется). - На устройствах с небольшим объемом ОЗУ запускайте задачи только в случае крайней необходимости. При необходимости используйте WorkManager
WorkRequestтолько для обновления рекомендаций после воспроизведения и старайтесь делать это, пока приложение ещё открыто. Определите WorkManager
Constraints, чтобы система выполняла ваши задачи в подходящее время:
Kotlin
Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .setRequiresStorageNotLow(true) .setRequiresDeviceIdle(true) .build()
Java
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresStorageNotLow(true)
.setRequiresDeviceIdle(true)
.build()
- Если вам нужно регулярно выполнять задачи (например, чтобы обновлять раздел Что посмотреть дальше на основе действий пользователя в вашем приложении на другом устройстве), следите за тем, чтобы потребление памяти задачей не превышало 30 МБ.
Общие рекомендации по памяти
Ниже приведены общие рекомендации по разработке приложений для Android.
- Минимизируйте выделение объектов, оптимизируйте повторное использование объектов и своевременно освобождайте неиспользуемые объекты.
- Не сохраняйте ссылки на объекты, особенно на растровые изображения.
- Не используйте
System.gc()и прямые вызовы для освобождения памяти, поскольку они мешают системному процессу управления памятью. Например, на устройствах с zRAM принудительный вызовgc()может временно увеличить использование памяти из-за сжатия и распаковки. - Используйте
LazyList, как показано в браузере каталога в Compose илиRecyclerViewв устаревшем наборе инструментов Leanback UI, чтобы повторно использовать представления и не создавать заново элементы списка. - Кешируйте локально элементы, полученные от внешних поставщиков контента, которые вряд ли изменятся, и задайте интервалы обновления, которые не позволят выделить дополнительную внешнюю память.
- Проверьте, нет ли утечек памяти.
- Обратите внимание на типичные случаи утечки памяти, например ссылки в анонимных потоках, перераспределение видео буферов, которые никогда не освобождаются, и другие подобные ситуации.
- Используйте дамп кучи, чтобы отлаживать утечки памяти.
- Создайте базовые профили, чтобы свести к минимуму количество компиляций JIT при холодном запуске приложения.
Что такое прямое освобождение памяти
Когда приложение для Android TV запрашивает память и система перегружена, ядро Linux, на котором основана ОС Android, может использовать прямое освобождение памяти.
Процесс включает полную приостановку любого потока выделения в ожидании освобождения страниц памяти. Это происходит, когда фоновое освобождение памяти не может поддерживать достаточный объем памяти.
Это может привести к заметным паузам или временным зависаниям в удобстве использования, поскольку система приостанавливает выделение потоков до тех пор, пока не будет доступно достаточно памяти. В этом смысле выделение потоков не ограничивается вызовами кода приложения, такими как malloc(). Например, память должна быть выделена для страницы в кодовых страницах.
Сводка по инструментам
- Используйте профилировщик памяти Android Studio, чтобы проверить потребление памяти во время использования.
- Используйте heapdump, чтобы проверить выделение определенных объектов и растровых изображений.
- Используйте профилировщик нативной памяти, чтобы проверить выделение памяти, не относящейся к Java или Kotlin.
- Используйте Android GPU Inspector, чтобы проверить распределение графических ресурсов.