Connect with us

Разработка

На 65% меньше APK и на 70% меньше потребление памяти: оптимизация приложения для Android — память

Опубликовано

/

     
     

Мы уже рассказывали об уменьшении размера APK в предыдущей статье. В этой мы подробно рассмотрим оптимизацию памяти.

Почему память приложений важна?

Эффективные приложения, использующие минимум памяти, лучше работают, экономят ресурсы устройства и продлевают время автономной работы. Они обеспечивают плавную работу пользователя и их больше выбирают в магазинах приложений. Такие приложения совместимы с широким спектром устройств.

Методы трассировки памяти

Для отслеживания памяти приложения можно воспользоваться одним из следующих подходов:

1. Команда ADB

Чтобы получить данные о памяти приложения в конкретном инстансе, выполните в терминале следующую команду:

adb shell dumpsys meminfo appPackageName

Примечание: Замените appPackageName на фактическое имя пакета приложения, которое вы хотите отслеживать.

На 65% меньше APK и на 70% меньше потребление памяти: оптимизация приложения для Android - память

На приведенном выше скриншоте видно, что приложение использует 42 МБ памяти.

Преимущества:

  • Получение данных о памяти любого приложения на устройстве.

Ограничения:

  • Невозможно отслеживать изменения памяти в виде графиков, основанных на взаимодействии с пользователем, как это делает Android Profiler.

2. Android Profiler

Шаги для запуска профилировщика: View (на верхней панели) -> Tool Windows -> Profiler -> Нажмите «+» -> Выберите устройство и пакет.

Преимущества:

  • Профилировщик Android позволяет отслеживать поведение во время выполнения и потребление памяти в зависимости от использования приложения.
  • Он позволяет получить полное представление о памяти приложения.

Ограничения:

  • Можно профилировать только «отлаживаемые приложения».

На 65% меньше APK и на 70% меньше потребление памяти: оптимизация приложения для Android - память

Примечание: Обратите внимание, что в этом блоге мы не будем рассматривать анализ памяти. Для получения более подробной информации обратитесь к официальной документации, доступной здесь.

Наше приложение в значительной степени ориентировано на работу с изображениями, и мы заметили, что после прокрутки 47 изображений объем памяти приложения превысил 500 МБ (как показано на скриншоте выше). Это повышает риск возникновения Out-of-Memory Exceptions (OOM). Мы осознали необходимость оптимизации памяти для улучшения пользовательского опыта и предприняли следующие шаги.

1. Изменение цветового формата пикселей

Пиксельный цветовой формат (Pixel Color Format), также известный как формат пикселя, определяет способ хранения в памяти цветовой информации для каждого пикселя изображения. Он определяет расположение красной, зеленой, синей и альфа (прозрачности) компонент, что влияет на качество цветопередачи и производительность рендеринга.

В Android поддерживается несколько цветовых форматов. Однако ниже мы остановимся на наиболее часто используемых.

  • ARGB_8888 (32 бита на пиксель) — 8 бит для альфы (прозрачности), 8 бит для красного, 8 бит для зеленого и 8 бит для синего цветов
  • RGB_565 (16 бит на пиксель) — 5 бит для красного, 6 бит для зеленого и 5 бит для синего, альфа отсутствует.

На 65% меньше APK и на 70% меньше потребление памяти: оптимизация приложения для Android - память
Как видно из приведенного изображения, разница между RGB 565 и ARGB 8888 едва заметна, но при использовании RGB 565 память сокращается примерно на 50%.

Для рендеринга изображений мы используем библиотеку Glide. По умолчанию Glide использует для загрузки изображений цветовой формат ARGB 8888. Однако Glide предоставляет возможность гибко настраивать предпочтительный формат цвета пикселей.

@GlideModule
class CustomGlideModule : AppGlideModule() {
    override fun applyOptions(context: Context, builder: GlideBuilder) {
        builder.setDefaultRequestOptions(RequestOptions().format(DecodeFormat.PREFER_RGB_565))
    }
}

Как показано выше, мы изменили формат цвета по умолчанию на RGB 565 на уровне приложения. Такое изменение конфигурации приводит к снижению потребления памяти на пиксель на 50%. Для проектов, в которых большое место занимают изображения, это решение имеет большое значение.

Примечание: RGB 565 имеет определенные ограничения, такие как незначительные цветовые вариации и отсутствие поддержки прозрачности. Это может привести к снижению точности цветопередачи для конкретных изображений, поэтому выбор следует делать исходя из конкретных требований приложения.

2. Изменение стратегии Glide DiskCacheStrategy

Стратегия DiskCacheStrategy в первую очередь определяет, каким образом изображения будут кэшироваться на устройстве.

Ранее мы использовали DiskCacheStrategy.All. Однако, проведя некоторые исследования, мы поняли, что DiskCacheStrategy.Resource больше соответствует нашим специфическим потребностям.

  • DiskCacheStrategy.ALL — кэширование всех версий изображения.
  • DiskCacheStrategy.Resource — кэширует на диске только конечное изображение после всех преобразований (например, изменений размера, кадрирования). Эта стратегия идеально подходит для случаев, когда необходимо кэшировать не исходные данные, а полностью обработанное изображение.

Дополнительные стратегии DiskCacheStrategies можно найти в официальной документации.

Например, в таких приложениях, как WhatsApp, изображения часто отображаются в сжатом формате. Но иногда пользователи могут захотеть поделиться этими изображениями в исходном виде с высоким разрешением. В таких случаях DiskCacheStrategy.Resource не подойдет.

Таким образом, выбор стратегии DiskCacheStrategy зависит от конкретных требований вашего приложения.

3. Модификация значения OffscreenPageLimit

Первоначально, чтобы минимизировать задержки и предотвратить появление пустых экранов, мы установили для ViewPager значение OffscreenPageLimit равным 3. Мы предполагали, что ViewPager будет кэшировать предыдущую, текущую и следующую страницы. Однако при дальнейшем исследовании мы обнаружили, что на самом деле он кэширует три предыдущие, текущую и следующую страницы, в результате чего в памяти хранится в общей сложности 7 изображений высокого разрешения большого размера.

На основании проведенного анализа мы решили уменьшить значение параметра OffscreenPageLimit до 1. Это не только уменьшило потребление памяти, но и обеспечило бесперебойную работу приложения без каких-либо проблем с задержками.

viewPager.offscreenPageLimit = 1

4. Очистка кэша в onViewRecycled

onViewRecycled — это колбек метод в RecyclerView.Adapter, который срабатывает при переработке представления. Он обычно используется в Glide для отмены загрузки изображений, что позволяет оптимизировать использование памяти и сети.

override fun onViewRecycled(holder: ChildBingeHolder) {
        GlideApp.with(context).clear(yourView)
}

Очистка кэша View при переработке в адаптере позволяет освободить память.

5. Указание размера изображения

У нас был ImageView размером 64×64 px (маленький), но наш API предоставлял изображение размером 512×512 px (большое). Декодирование такого большого изображения для маленького заполнителя неэффективно с точки зрения использования памяти и производительности приложения.

На 65% меньше APK и на 70% меньше потребление памяти: оптимизация приложения для Android - память

Для решения этой проблемы мы указали ширину и высоту представления, чтобы обеспечить корректное масштабирование изображения, а не загружать в память слишком большое изображение.

Glide.with(this)
        .load(IMAGE_URL)
        .override(targetWidth, targetHeight)
        .into(imageView)

Использование функции override() позволяет значительно сократить потребление памяти за счет указания нужных размеров изображения, что особенно удобно при работе с большими изображениями или несколькими изображениями, отображаемыми одновременно.

6. Обработка onTrimMemory

Реализация onTrimMemory(int) позволяет постепенно освобождать память в зависимости от системных ограничений. Это повышает отзывчивость системы и удобство работы пользователя, позволяя процессу работать дольше. Без обрезания ресурсов система может убить ваш кэшированный процесс, что потребует от вашего приложения перезапуска и восстановления состояния при возвращении пользователя.

Примечание: Приведенная ниже обработка уровней памяти соответствует специфическим требованиям нашего приложения. Мы настраиваем уровни памяти по мере необходимости. Документация по уровням памяти.

override fun onTrimMemory(level: Int) {
    //Memory Levels documentation : https://developer.android.com/reference/android/content/ComponentCallbacks2
    // TRIM_MEMORY_COMPLETE & TRIM_MEMORY_MODERATE are the levels which are called when the app is in background
    if (level == android.content.ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
        GlideApp.get(this).clearMemory()
    } else if (level == android.content.ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
        GlideApp.with(this).onTrimMemory(TRIM_MEMORY_MODERATE)
    }
}

Результат

Выполнив указанные шаги и внеся некоторые незначительные коррективы, мы успешно добились значительных улучшений в потреблении приложением памяти.

На 65% меньше APK и на 70% меньше потребление памяти: оптимизация приложения для Android - память

Использование памяти уменьшилось с 515 МБ при прокрутке 47 изображений до 137 МБ (более чем на 70%) при прокрутке 67 изображений, как показано в таблице. Это улучшение позволяет нам добавлять в приложение больше изображений, не опасаясь нехватки памяти.

На 65% меньше APK и на 70% меньше потребление памяти: оптимизация приложения для Android - память

Заключение

Таким образом, наша оптимизация позволила значительно уменьшить размер APK и оптимизировать потребление памяти. Эти усилия не только улучшили пользовательский опыт, но и проложили путь к более эффективной работе продукта, позволив нам предоставлять клиентам более качественное, быстрое и оптимизированное приложение.

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.

Наши партнеры:

LEGALBET

Мобильные приложения для ставок на спорт
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: