Android lru cache bitmap

Хранение изображений с помощью LruCache

При разработке приложений разработчик может столкнуться с такой проблемой, когда ему нужно очень часто работать с изображениями. Система каждый раз должна их загружать, обрабатывать, затем, после окончания использования, удалять, либо удалять тогда, когда это не нужно, с помощью сборщика мусора. Чтобы не выполнять все эти операции по кругу достаточно будет кэшировать готовые изображения в памяти устройства и по мере необходимости обращаться к ним. Для таких целей в Android отличным решением является использование кэш-памяти (LruCache) и дискового кэша (DiskLruCache). Оба этих способа основаны на использовании LRU-кэша.

LRU-кэшем называется стратегия кэширования, в которой используется политика Вытеснения давно неиспользуемых (Least Recently Used). То есть, когда кэш заполнен, и мы хотим добавить в него новые данные, из него автоматически будут удалены те данные, которые не использовались наиболее длительное время.

Класс LruCache был добавлен в Android начиная с версии Android 3,1 (API 12), но также доступен через библиотеку поддержки, начиная с версии Android 1.6. Основной его задачей является повторное использование объектов, создание которых затратно по ресурсам. Например, вы можете загрузить изображение из Интернета и поместить его в кэш, чтобы в будущем обращаться только к кэш-памяти.

В нашем приложении «Менеджер системных приложений» при прокрутке списка приложений метод onBindViewHolder() постоянно обращается к PackageManager для загрузки иконок приложений. Правильным решением будет после первой загрузки иконок помещать их в кэш-память, чтобы впоследствии обращаться только к ней.

Для этого создадим класс IconCache, наследующий от LruCache, который будет реализовывать размещение иконок в кэше и вызов их из него.

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

При создании экземпляра LruCache в параметры необходимо передать размер кэша. Важно указать его верно, поскольку слишком маленький размер приведёт к тому, что операции с кэшем станут более затратными, чем текущие, а слишком большой — оставить мало памяти для других приложений на устройстве или даже вызвать исключение java.lang.outOfMemory. Поэтому при определении размера кэша важно сначала определить размер памяти устройства. Единого способа для этого нет, поэтому мы будем использовать класс метод getMemoryClass() класса ActivityManager.

Поскольку в приложении используется MVP паттерн, инициализация происходит на уровне Presenter и затем экземпляр IconCache передаётся в фоновый поток, принимающий запросы на загрузку иконок. Подробнее о MVP можно почитать в предыдущей статье.

Затем, когда ViewHolder запрашивает иконку для приложения, в потоке проверяется, есть ли в кэше объект с данным именем пакета. Если он есть, то берём из кэша и возвращаем результат, а если нет — вызываем PackageManager, загружаем иконку, возвращаем её в главный поток и помещаем в кэш.

О том, как реализован фоновый поток, можно почитать в данной статье.

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

Источник

Srain

LRU cache in Android

LRU cache

LRU cache is very convinient to manager a list of data which has a limit space requirement.

LRU cache managers an object list. Each time a valued is accessed, it is moved to the head of the list. When a value is put into the cache, the value at the end of the list may be evicted.

There are two kinds of LruCache in Android: LruCache and DiskLruCache , the fisrt can be used to manager an set of objects, the later can be used to manager files.

Читайте также:  Монитор процессора для андроид
When to use

A scenario that a LruCache can be used is to manager the quota of disk cache and memory cache when loading a network image.

After an image is loaded from the remote server, commonly it will be writen to disk for further use. When the image is required to display in the ImageView we will decode the bitmap data from this file. The decoding operation will take a long time. To avoid the same image to be decode for multiple time for the bitmap data, the bitmap data will be cached in the memory after the first time it is decoded.

But the memory and the storage space is very limited, we can not take too much of that. Each time a file is writen to storage of a bitmap data is cached into memory, we must check the total size of the cached data and remove the eldest objects if nessery.

Here, the LRU Cache will make things easy.

Memory LruCache

LruCache is in the support V4 package. It is very simple.

The LruCache has a LinkedHashmap inside, which is constructed by

The third parameter allow the LinkedHashmap to put the element to the head of the linked list after accessed, both by get and set .

  1. sizeOf(K key, V value) method return the size of each count which will be used to calculate the size of the whole size. > The default implementions returns 1, which make the LRU cache only manager a limit amount of objects.
  2. The method put(K key, V value) cache the value to the cache and check the size.

The method get(K key) will return the value for the key if it is exist in the cache.

When a value is returned, it will move to the head of the queue, this is grantened by the LinkedHashmap ;

If the value is not exist, method create(K key) will be called to create the value. If a the value is created, it will be put into cache and the limit size will be checked. This will make the code simple.

entryRemoved will be call when an object is removed when the new value conrespong value of this key is put into cache or the total size of the cache has exceed the limit.

We can make a limit cache to store BitmapData:

DiskLurCache

The DiskLruCache has an implementaion in JB source.

Like LruCache , it has a LinkedHashmap inside to manage the access order of the cached files.

There are some classes where are used to managed the info of the cached files:

Источник

Android Bitmap Cache

Introduction

Memory efficient bitmap caching: This is particularly important if your application uses animations as they will be stopped during GC cleanup and make your application appears sluggish to the user. A cache allows reusing objects which are expensive to create. If you load on object into memory, you can think of this as a cache for the object.Working with bitmap in android is tricky.It is more important to cache the bimap if you are going to use it repeatedly.

Syntax

  • LruCache mMemoryCache;//declaration of LruCache object.
  • void addBitmapToMemoryCache(String key, Bitmap bitmap)<>//declaration of generic method adding bitmap into cache memory
  • Bitmap getBitmapFromMemCache(String key)<>//declaration of generic method for get bimap from cache.

Parameters

Parameter Details
key key to store bitmap in memory cache
bitmap bitmap value which will cache into memory

Bitmap Cache Using LRU Cache

LRU Cache

The following example code demonstrates a possible implementation of the LruCache class for caching images.

Here string value is key for bitmap value.

For add bitmap to the memory cache

For get bitmap from memory cache

For loading bitmap into imageview just use getBitmapFromMemCache(«Pass key»).

Источник

Кэширование растровых изображений

Этот урок научит вас

Вы также должны прочитать

Попробуйте

Загрузка единственного растрового изображения в ваш пользовательский интерфейс является простой задачей, однако все становится более сложным, если вам необходимо загрузить большой набор изображений одновременно. Во многих случаях (как например с компонентами, такими как ListView , GridView или ViewPager ), общее количество изображений на экране в сочетании с изображениями, которые могут в скором времени появятся на экране в результате прокрутки, по существу неограниченно.

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

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

Использование кэша памяти

Кэш-память обеспечивает быстрый доступ к растровым изображениям за счет использования ценной памяти приложения. LruCache класс (также доступен в Библиотеке Поддержки для использования начиная c API Уровень 4) особенно хорошо подходит для задачи кэширования растровых изображений, удерживая недавно загруженные объекты с помощью сильных ссылок в LinkedHashMap , удаляя элементы, которые не использовались дольше всего, если кэш превышает отведенный размер.

Примечание: В прошлом, популярная реализация кэш-памяти растрового кэша была на основе SoftReference или WeakReference , однако сейчас не рекомендуется использовать такую реализацию. Начиная с Android 2.3 (API Уровень 9) сборщик мусора стал более агрессивной к сбору мягких и слабых ссылок, что делает их довольно неэффективным. Кроме того, до Android 3.0 (API Уровень 11), внутренние данные растрового изображения хранились непосредственно в оперативной памяти, которая не всегда освобождалась предсказуемым образом, что могло привести к быстрому превышению своего предела памяти приложением, и как следствие к краху приложения.

Чтобы выбрать подходящий размер для LruCache , необходимо принять во внимание ряд факторов, например:

  • Какой объем памяти необходим для остальной часть вашей деятельности и/или приложения?
  • Сколько изображений будет на экране сразу? Сколько должно быть доступно и готово для появления на экране?
  • Каков размер экрана и плотность устройства? Устройствам с дисплеями сверхвысокой плотности (xhdpi), таким как Galaxy Nexus понадобится больший кэш для хранения такого же количества изображений в памяти по сравнению с устройством, таким как Nexus S (hdpi).
  • Какого размера и конфигурации растровые изображения, и, следовательно, сколько памяти будет каждый из них занимать?
  • Как часто изображения будут необходимы ? Будут ли некоторые необходимы чаще, чем другие? Если это так, может быть, вы хотите хранить некоторые элементы в памяти всегда или даже иметь несколько LruCache объектов для различных групп растровых изображений.
  • Можете ли вы сбалансировать качество и количество? Иногда может быть более целесообразным хранить большое количество растровых изображений более низкого качества, потенциально загружая версию с более высоким качеством в другом фоновом потоке.

Здесь нет конкретного размера или формулы, которая подходит для всех приложений, вы должны анализировать то, как вы будете использовать кэш, и придумать подходящее решение. Кэш, который слишком мал, приводит к дополнительным накладным расходам без какой-либо выгоды, кэш, который является слишком большой может вновь привести к исключению java.lang.OutOfMemory и оставить для работы остальной части вашего приложении маленький объем памяти.

Вот пример настройки LruCache для растровых изображений:

Примечание: В этом примере, одна восьмая памяти приложения выделяется для нашего кэша. На нормальном/hdpi устройстве это минимум около 4 Мб (32/8). Полноэкранный GridView заполнен изображениями на устройстве с разрешением 800×480 будет использовать около 1,5 Мб (800*480*4 байта), так что будет кэшироваться как минимум около 2,5 страниц изображений в памяти.

При загрузке растрового изображения в ImageView LruCache проверяется первым. Если запись найдена, она сразу же используется для обновления ImageView , в противном случае создается фоновый поток для обработки изображения:

BitmapWorkerTask также должен быть обновлен, чтобы добавлять записи в кэш-память:

Использование дискового кэша

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

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

Примечание: ContentProvider может быть более подходящим местом для хранения кэшированных изображений, если они нужны более чаще, как например в приложении Галерея.

Примере кода этого класса использует DiskLruCache реализацию, которая взята из исходного кода Android. Вот обновленный пример кода, который добавляет дисковый кэш в дополнение к существующей кэш-памяти:

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

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

Обработка изменений в конфигурации

Изменения конфигурации выполнения, такие как изменение ориентации экрана, заставляют Android уничтожить и перезапустить выполняемую деятельность с новой конфигурацией (Для получения дополнительной информации о данном поведении см. Обработка изменений времени выполнения). Вы хотите избежать необходимости обрабатывать все ваши изображения снова, чтобы пользователь имеет плавный и быстрый опыт, когда происходит изменение конфигурации.

К счастью, у вас есть хорошая кэш-память растровых изображений, которую вы построили в разделе Использование кэша памяти . Этот кэш может быть передан новому экземпляру деятельности с помощью Fragment , который сохранится с помощью вызова setRetainInstance(true) ). После того, как деятельность будет создана заново, этот сохранившийся Fragment будет повторного присоединен, и вы получите доступ к существующему объекту кэша, что позволяет быстро извлекать изображения и заново использовать в ImageView объектах.

Вот пример удерживания LruCache объекта при изменении конфигурации, используя Fragment :

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

Источник

Читайте также:  Как увеличить файл подкачки андроид без root
Оцените статью