Оптимизация памяти имеет решающее значение для обеспечения бесперебойной работы, предотвращения сбоев приложений и поддержания стабильности системы и работоспособности платформы. Хотя использование памяти следует отслеживать и оптимизировать в каждом приложении, контентные приложения для телевизоров имеют специфические проблемы, которые отличаются от типичных приложений Android для портативных устройств.
Высокое потребление памяти может привести к проблемам в работе приложений и системы, в том числе:
- Само приложение может начать работать медленно или с задержками, а в худшем случае — полностью закрыться.
- Системные службы, доступные пользователю (регулировка громкости, панель настроек изображения, голосовой помощник и т. д.), начинают сильно тормозить или могут вообще не работать.
- Процесс демона LMK (low memory killer) может реагировать на высокую нагрузку на память, завершая наименее важные процессы; затем эти компоненты могут вскоре перезапуститься, вызывая всплески дальнейшей конкуренции за ресурсы, что может напрямую повлиять на работу приложения на переднем плане.
- Переход к панели запуска может значительно затянуться, и до завершения перехода приложение на переднем плане будет казаться неработоспособным.
- Система может начать использовать прямое высвобождение памяти , временно приостанавливая выполнение потока в ожидании выделения памяти. Это может произойти с любым потоком, например, с основным потоком или потоками, связанными с кодеком, что потенциально может привести к выпадению кадров аудио и видео, а также к сбоям в пользовательском интерфейсе.
Вопросы памяти в телевизионных устройствах
Телевизоры, как правило, имеют значительно меньше памяти, чем телефоны или планшеты. Например, конфигурация, которую мы видим на телевизорах, включает 1 ГБ оперативной памяти и разрешение видео 1080p . В то же время большинство телевизионных приложений имеют схожие функции, а значит, и схожую реализацию и общие проблемы. Эти две ситуации создают проблемы, не встречающиеся на других типах устройств и в приложениях:
- Приложения Media TV обычно состоят как из сетки изображений, так и из полноэкранных фоновых изображений , что требует загрузки большого количества изображений в память за короткий промежуток времени.
- Приложения для телевизоров воспроизводят мультимедийные потоки , для чего требуется выделить определенный объем памяти для воспроизведения видео и аудио, а также значительные буферы для обеспечения плавной работы.
- Дополнительные функции воспроизведения мультимедиа (перемотка, смена эпизода, переключение звуковых дорожек и т. д.) могут создавать дополнительную нагрузку на память, если реализованы неправильно.
Разберитесь в телевизионных устройствах.
Данное руководство в основном посвящено использованию памяти приложениями и целевым показателям памяти на устройствах с небольшим объемом оперативной памяти.
При выборе телевизионных устройств следует учитывать следующие характеристики:
- Память устройства : объем оперативной памяти (RAM), установленной в устройстве.
- Разрешение пользовательского интерфейса устройства : разрешение, используемое устройством для отображения пользовательского интерфейса операционной системы и приложений; обычно оно ниже, чем разрешение видеокарты устройства.
- Разрешение видео : максимальное разрешение, с которым устройство может воспроизводить видео.
Это позволяет классифицировать различные типы устройств и определить, как они должны использовать память.
Краткое описание телевизионных устройств
| Память устройства | Разрешение видео устройства | Разрешение пользовательского интерфейса устройства | 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).
- Другие ситуации, в которых производитель оборудования определил флаг
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 состоит из выделений памяти, выполняемых нативными приложениями, Java-приложениями и стеком, в профилировщике памяти Android Studio. Используйте ActivityManager.isLowMemoryDevice() , чтобы проверить, испытывает ли устройство нехватку памяти, и адаптируйтесь к этой ситуации, следуя этим рекомендациям.
- СМИ:
- Укажите переменный размер буферов мультимедиа в зависимости от объема оперативной памяти устройства и разрешения воспроизведения видео . Размер буфера должен соответствовать 1 минуте воспроизведения видео:
- 40-60 МБ на 1 ГБ / 1080p
- 60-80 МБ на 1,5 ГБ / 1080p
- 80-100 МБ на 1,5 ГБ / 2160p
- 100-120 МБ на 2 ГБ / 2160p
- Освобождайте память для воспроизведения медиафайлов при изменении эпизода , чтобы предотвратить увеличение общего объема анонимной памяти.
- Немедленно освобождайте и останавливайте медиаресурсы при остановке приложения: используйте обратные вызовы жизненного цикла активности для управления аудио- и видеоресурсами. Если ваше приложение не является аудиоприложением, остановите воспроизведение при вызове
onStop()в ваших активностях, сохраните всю выполняемую работу и настройте освобождение ресурсов. Для планирования задач, которые могут понадобиться позже, см. раздел «Задания и оповещения» .- Для обработки вызовов жизненного цикла Activity можно использовать компоненты, учитывающие жизненный цикл, такие как
LiveDataиLifecycleOwner. - Чтобы учесть жизненный цикл вашей работы, вы также можете использовать корутины Kotlin и потоки Kotlin .
- Для обработки вызовов жизненного цикла Activity можно использовать компоненты, учитывающие жизненный цикл, такие как
- Обращайте внимание на объем памяти буфера при перемотке видео : разработчики часто выделяют дополнительные 15-60 секунд будущего времени при перемотке, чтобы видео было готово для пользователя, но это создает дополнительную нагрузку на память. В целом, не следует использовать более 5 секунд будущего буфера, пока пользователь не выберет новую позицию в видео. Если вам строго необходимо предварительно зарезервировать дополнительное время при перемотке, убедитесь, что:
- Заранее выделите буфер поиска и используйте его повторно.
- Размер буфера не должен превышать 15-25 МБ (в зависимости от объема памяти устройства).
- Укажите переменный размер буферов мультимедиа в зависимости от объема оперативной памяти устройства и разрешения воспроизведения видео . Размер буфера должен соответствовать 1 минуте воспроизведения видео:
- Распределения:
- Воспользуйтесь рекомендациями по использованию графической памяти , чтобы избежать дублирования изображений в анонимной памяти.
- Изображения часто потребляют больше всего памяти, поэтому их дублирование может создавать значительную нагрузку на устройство. Это особенно актуально при интенсивной навигации по табличным представлениям изображений.
- Освободите выделенные ресурсы, удалив ссылки на них при перемещении экранов : убедитесь, что не осталось ссылок на растровые изображения и объекты.
- Воспользуйтесь рекомендациями по использованию графической памяти , чтобы избежать дублирования изображений в анонимной памяти.
- Библиотеки:
- При добавлении новых библиотек следует учитывать выделение памяти , поскольку они могут загружать и другие библиотеки, которые также могут выделять память и создавать привязки (Bindings) .
- Сетевые технологии:
- Не выполняйте блокирующие сетевые вызовы во время запуска приложения , так как они замедляют время запуска приложения и создают дополнительную нагрузку на память при запуске, когда память особенно ограничена из-за нагрузки на приложение. Сначала покажите экран загрузки или заставку, а сетевые запросы выполняйте после того, как пользовательский интерфейс будет готов.
Переплеты
Привязка сервиса приводит к дополнительным накладным расходам на память, поскольку она загружает в память другие приложения или увеличивает потребление памяти привязанным приложением (если оно уже находится в памяти) для выполнения вызова API. В результате это уменьшает доступную память для приложения, работающего на переднем плане. При привязке сервиса следите за тем, когда и как долго вы используете привязку. Обязательно освобождайте привязку, как только она перестает быть необходимой.
Типичные варианты переплета и лучшие практики:
- API проверки целостности игры : используется для проверки целостности устройства.
- Проверьте целостность устройства после экрана загрузки и перед воспроизведением мультимедиа.
- Перед воспроизведением контента освободите ссылки на PlayIntegrity
StandardIntegrityManager.
- Библиотека Play Billing : используется для управления подписками и покупками в Google Play.
- После загрузки библиотеки необходимо инициализировать ее и выполнить все расчеты по оплате до начала воспроизведения медиафайлов.
- Используйте
BillingClient.endConnection()после завершения работы с библиотекой и всегда перед воспроизведением видео или мультимедиа. - Используйте
BillingClient.isReady()иBillingClient.getConnectionState(), чтобы проверить, было ли соединение разорвано, на случай, если потребуется повторно выполнить какие-либо действия по выставлению счетов, а затем снова вызовитеBillingClient.endConnection()после завершения.
- GMS FontsProvider
- На устройствах с небольшим объемом оперативной памяти предпочтительнее использовать автономные шрифты, а не поставщики шрифтов, поскольку загрузка шрифтов обходится дорого, и FontsProvider будет привязывать сервисы для выполнения этой задачи.
- Библиотека Google Ассистента : Иногда используется для поиска и поиска внутри приложений; по возможности замените эту библиотеку.
- Для приложений Leanback : используйте преобразование текста в речь Gboard или библиотеку androidx.leanback .
- При внедрении поиска следуйте рекомендациям по его проведению.
- Примечание: функция leanback устарела , и приложениям следует перейти на использование TV Compose.
- Для приложений Compose :
- Используйте функцию преобразования текста в речь в Gboard для реализации голосового поиска.
- Внедрите функцию «Смотреть дальше» , чтобы сделать медиаконтент в вашем приложении доступным для поиска.
- Для приложений Leanback : используйте преобразование текста в речь Gboard или библиотеку androidx.leanback .
Передние службы
Службы переднего плана — это особый тип служб, связанных с уведомлениями. Эти уведомления отображаются в панели уведомлений на телефонах и планшетах, но у телевизоров панели уведомлений в том же смысле, что и у этих устройств. Даже если службы переднего плана полезны, поскольку они могут продолжать работать, пока приложение находится в фоновом режиме, приложения для телевизоров должны соответствовать следующим рекомендациям:
В Android TV и Google TV работа фоновых служб разрешена только после того, как пользователь покинет приложение:
- Для аудиоприложений : работа фоновых служб разрешена только после того, как пользователь покинет приложение для продолжения воспроизведения аудиодорожки. Службу необходимо немедленно остановить после завершения воспроизведения аудио.
- Для любых других приложений: все службы переднего плана должны быть остановлены , как только пользователь покинет ваше приложение , поскольку не будет уведомления о том, что приложение все еще работает и потребляет ресурсы.
- Для фоновых задач , таких как обновление рекомендаций или списка «Смотреть дальше» , используйте
WorkManager.
Задания и оповещения
WorkManager — это современный API Android для планирования фоновых повторяющихся задач. WorkManager будет использовать новый JobScheduler если он доступен (SDK 23+), и старый AlarmManager если он недоступен. Для оптимального выполнения запланированных задач на телевизоре следуйте этим рекомендациям:
- В SDK 23 и выше следует избегать использования API
AlarmManager, особенно методовAlarmManager.set(),AlarmManager.setExact()и подобных им, поскольку они не позволяют системе определить подходящее время для выполнения задач (например, когда устройство находится в режиме ожидания). - На устройствах с небольшим объемом оперативной памяти избегайте запуска заданий, если это не является абсолютно необходимым. При необходимости используйте WorkManager
WorkRequestтолько для обновления рекомендаций после воспроизведения и старайтесь делать это, пока приложение еще открыто. Настройте
ConstraintsWorkManager, чтобы система могла запускать ваши задания в подходящее время:
Котлин
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, чтобы повторно использовать представления и не создавать элементы списка заново. - Кэшировать локально элементы, считываемые из внешних источников контента, которые вряд ли изменятся, и определять интервалы обновления, которые предотвращают выделение дополнительной внешней памяти.
- Проверьте наличие возможных утечек памяти.
- Остерегайтесь типичных случаев утечек памяти , таких как ссылки внутри анонимных потоков, перераспределение видеобуферов, которые никогда не освобождаются, и другие подобные ситуации.
- Используйте дамп кучи для отладки утечек памяти.
- Создайте базовые профили , чтобы свести к минимуму объем компиляции «на лету», необходимой при запуске приложения с нуля.
Понимание процесса восстановления прямой памяти
Когда приложение для Android TV запрашивает память, а система испытывает нехватку памяти, ядру Linux, лежащему в основе Android, может потребоваться использовать прямое высвобождение памяти .
Этот процесс включает в себя полную приостановку любого потока, выделяющего память, в ожидании освобождения страниц памяти. Это происходит, когда фоновое высвобождение памяти не в состоянии поддерживать достаточный пул памяти заблаговременно.
Это может привести к заметным паузам или рывкам в работе пользователя, поскольку система приостанавливает выделение потоков до тех пор, пока не будет выделено достаточно памяти. В этом смысле выделение потоков не ограничивается вызовами кода приложения, такими как malloc() ; память необходимо выделять, например, для страниц кода.
Краткое описание инструментов
- Используйте инструмент профилирования памяти Android Studio , чтобы проверить потребление памяти во время работы.
- Используйте heapdump для проверки выделения памяти для конкретных объектов и битовых карт.
- Используйте встроенный профилировщик памяти для проверки выделения памяти не в Java или Kotlin.
- Используйте Android GPU Inspector для проверки распределения графических ресурсов.