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

Оптимизация памяти имеет решающее значение для обеспечения плавной работы, предотвращения сбоев приложений и поддержания стабильности системы и работоспособности платформы. Хотя использование памяти должно контролироваться и оптимизироваться в каждом приложении, контентные приложения для ТВ- устройств имеют определенные проблемы, которые отличаются от типичных приложений 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 , которая по умолчанию отключена. Включение этой функции позволяет избежать дублирования битовых карт, которые в противном случае находились бы как в графической памяти, так и в анонимной памяти.
  • Избегайте промежуточных рендеров и повторных рендеров
    • Их можно определить с помощью Android GPU Inspector :
    • В разделе «Текстуры» вы найдете изображения, которые представляют собой шаги к финальному рендеру, а не просто элементы, его формирующие. Обычно это так называемый «промежуточный рендер».
    • Для приложений Android SDK вы часто можете удалить их, используя флаг макета forceHasOverlappedRendering:false , чтобы отключить промежуточные рендеры для этого макета.
    • Ознакомьтесь с полезным ресурсом «Избегайте перекрывающихся рендеров» по ​​перекрывающимся рендерам.
  • По возможности избегайте загрузки изображений-заполнителей , используйте @android:color/ или @color для текстур-заполнителей.
  • Избегайте компоновки нескольких изображений на устройстве, когда композиция может быть выполнена офлайн. Предпочитайте загружать отдельные изображения, а не делать компоновку изображений из загруженных изображений
  • Чтобы лучше работать с растровыми изображениями, следуйте руководству «Обработка растровых изображений» .

Аноним+Обмен памятью

Anon+Swap состоит из выделений Native + Java + Stack в профилировщике памяти Android Studio. Используйте ActivityManager.isLowMemoryDevice() , чтобы проверить, ограничено ли устройство памятью, и адаптируйтесь к этой ситуации, следуя этим рекомендациям.

  • СМИ:
    • Укажите переменный размер для медиа-буферов в зависимости от оперативной памяти устройства и разрешения воспроизведения видео . Это должно учитывать 1 минуту воспроизведения видео:
      1. 40-60 МБ для 1 ГБ / 1080p
      2. 60-80 МБ для 1,5 ГБ / 1080p
      3. 80-100 МБ для 1,5 ГБ / 2160p
      4. 100-120 МБ для 2 ГБ / 2160p
    • Освобождайте выделенную медиапамять при смене эпизода, чтобы предотвратить увеличение общего объема анонимной памяти.
    • Освобождайте и останавливайте медиаресурсы немедленно , когда ваше приложение останавливается: используйте обратные вызовы жизненного цикла активности для обработки аудио- и видеоресурсов. Если вы не являетесь аудиоприложением, останавливайте воспроизведение, когда в ваших активностях происходит onStop() , сохраняйте всю выполняемую вами работу и настройте освобождение ресурсов. Чтобы запланировать работу, которая может понадобиться позже. См. раздел Задания и сигналы тревоги .
    • Обратите внимание на память буфера при поиске видео : разработчики часто выделяют дополнительные 15-60 секунд будущего контента при поиске видео, готового для пользователя, но это создает дополнительные накладные расходы памяти. В общем, не выделяйте более 5 секунд будущего буфера, пока пользователь не выберет новую позицию видео. Если вам строго необходимо предварительно буферизовать дополнительное время при поиске, убедитесь, что:
      • Выделите буфер поиска заранее и используйте его повторно.
      • Размер буфера не должен превышать 15-25 МБ (в зависимости от объема памяти устройства).
  • Распределения:
    • Используйте руководство по графической памяти, чтобы убедиться, что вы не дублируете изображения в анонимной памяти.
      • Изображения часто являются крупнейшим пользователем памяти, поэтому их дублирование может оказать большую нагрузку на устройство. Это особенно актуально при интенсивной навигации по сеткам изображений.
    • Освободите выделенную память, удалив ссылки на них при перемещении экранов : убедитесь, что не осталось ссылок на растровые изображения и объекты.
  • Библиотеки:
    • Профилируйте выделение памяти из библиотек при добавлении новых, так как они также могут загружать дополнительные библиотеки, которые также могут выполнять выделение памяти и создавать привязки .
  • Нетворкинг:
    • Не выполняйте блокировку сетевых вызовов во время запуска приложения , они замедляют время запуска приложения и создают дополнительную нагрузку на память при запуске, когда память особенно ограничена загрузкой приложения. Сначала показывайте загрузочный экран или заставку и выполняйте сетевые запросы после того, как пользовательский интерфейс будет на месте.

Переплеты

Привязки приводят к дополнительным накладным расходам памяти , поскольку они переносят другие приложения в память или увеличивают потребление памяти привязанным приложением (если оно уже находится в памяти) для облегчения вызова 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 , чтобы сделать медиаконтент в вашем приложении легко обнаруживаемым.

Службы переднего плана

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() ; память должна быть выделена для страницы в кодовых страницах, например.

Обзор инструментов