Site icon AppTractor

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

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

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

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

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

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

1. Команда ADB

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

adb shell dumpsys meminfo appPackageName

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

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

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

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

2. Android Profiler

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

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

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

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

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

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

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

В 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 больше соответствует нашим специфическим потребностям.

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

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

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)
    }
}

Результат

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

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

Заключение

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

Источник

Exit mobile version