Android кеширование изображений - PullRequest
139 голосов
/ 22 декабря 2009

Как можно кэшировать изображения после их загрузки из Интернета?

Ответы [ 17 ]

173 голосов
/ 09 октября 2010

А теперь изюминка: используйте системный кеш.

URL url = new URL(strUrl);
URLConnection connection = url.openConnection();
connection.setUseCaches(true);
Object response = connection.getContent();
if (response instanceof Bitmap) {
  Bitmap bitmap = (Bitmap)response;
} 

Предоставляет как память, так и флэш-память, совместно используемую браузером.

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

65 голосов
/ 21 ноября 2010

Что касается элегантного решения connection.setUseCaches, приведенного выше: к сожалению, оно не будет работать без дополнительных усилий. Вам нужно будет установить ResponseCache, используя ResponseCache.setDefault. В противном случае HttpURLConnection будет игнорировать бит setUseCaches(true).

См. Комментарии вверху FileResponseCache.java для подробностей:

http://libs -for-android.googlecode.com / СВН / ссылка / COM / Google / Android / файловый кэш / FileResponseCache.html

(Я бы оставил это в комментарии, но мне явно не хватает ТАКОЙ кармы.)

27 голосов
/ 22 декабря 2009

Преобразуйте их в растровые изображения, а затем либо сохраните их в коллекции (HashMap, List и т. Д.), Либо вы можете записать их на SD-карту.

При хранении их в пространстве приложения с использованием первого подхода может потребоваться обернуть их вокруг java.lang.ref.SoftReference , в частности, если их число велико (чтобы они собирались во время кризиса ). Это может привести к перезагрузке.

HashMap<String,SoftReference<Bitmap>> imageCache =
        new HashMap<String,SoftReference<Bitmap>>();

запись их на SD-карту не требует перезагрузки; просто пользовательское разрешение.

25 голосов
/ 06 мая 2014

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

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

ШАГ 1: сделать имя класса ImagesCache. Я использовал Singleton object for this class

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;

public class ImagesCache 
{
    private  LruCache<String, Bitmap> imagesWarehouse;

    private static ImagesCache cache;

    public static ImagesCache getInstance()
    {
        if(cache == null)
        {
            cache = new ImagesCache();
        }

        return cache;
    }

    public void initializeCache()
    {
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() /1024);

        final int cacheSize = maxMemory / 8;

        System.out.println("cache size = "+cacheSize);

        imagesWarehouse = new LruCache<String, Bitmap>(cacheSize)
                {
                    protected int sizeOf(String key, Bitmap value) 
                    {
                        // The cache size will be measured in kilobytes rather than number of items.

                        int bitmapByteCount = value.getRowBytes() * value.getHeight();

                        return bitmapByteCount / 1024;
                    }
                };
    }

    public void addImageToWarehouse(String key, Bitmap value)
    {       
        if(imagesWarehouse != null && imagesWarehouse.get(key) == null)
        {
            imagesWarehouse.put(key, value);
        }
    }

    public Bitmap getImageFromWarehouse(String key)
    {
        if(key != null)
        {
            return imagesWarehouse.get(key);
        }
        else
        {
            return null;
        }
    }

    public void removeImageFromWarehouse(String key)
    {
        imagesWarehouse.remove(key);
    }

    public void clearCache()
    {
        if(imagesWarehouse != null)
        {
            imagesWarehouse.evictAll();
        }       
    }

}

ШАГ 2:

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

public class DownloadImageTask extends AsyncTask<String, Void, Bitmap>
{   
    private int inSampleSize = 0;

    private String imageUrl;

    private BaseAdapter adapter;

    private ImagesCache cache;

    private int desiredWidth, desiredHeight;

    private Bitmap image = null;

    private ImageView ivImageView;

    public DownloadImageTask(BaseAdapter adapter, int desiredWidth, int desiredHeight) 
    {
        this.adapter = adapter;

        this.cache = ImagesCache.getInstance();

        this.desiredWidth = desiredWidth;

        this.desiredHeight = desiredHeight;
    }

    public DownloadImageTask(ImagesCache cache, ImageView ivImageView, int desireWidth, int desireHeight)
    {
        this.cache = cache;

        this.ivImageView = ivImageView;

        this.desiredHeight = desireHeight;

        this.desiredWidth = desireWidth;
    }

    @Override
    protected Bitmap doInBackground(String... params) 
    {
        imageUrl = params[0];

        return getImage(imageUrl);
    }

    @Override
    protected void onPostExecute(Bitmap result) 
    {
        super.onPostExecute(result);

        if(result != null)
        {
            cache.addImageToWarehouse(imageUrl, result);

            if(ivImageView != null)
            {
                ivImageView.setImageBitmap(result);
            }
            else
            {

            }

            if(adapter != null)
            {
                adapter.notifyDataSetChanged();
            }
        }
    }

    private Bitmap getImage(String imageUrl)
    {   
        if(cache.getImageFromWarehouse(imageUrl) == null)
        {
            BitmapFactory.Options options = new BitmapFactory.Options();

            options.inJustDecodeBounds = true;

            options.inSampleSize = inSampleSize;

            try
            {
                URL url = new URL(imageUrl);

                HttpURLConnection connection = (HttpURLConnection)url.openConnection();

                InputStream stream = connection.getInputStream();

                image = BitmapFactory.decodeStream(stream, null, options);

                int imageWidth = options.outWidth;

                int imageHeight = options.outHeight;

                if(imageWidth > desiredWidth || imageHeight > desiredHeight)
                {   
                    System.out.println("imageWidth:"+imageWidth+", imageHeight:"+imageHeight);

                    inSampleSize = inSampleSize + 2;

                    getImage(imageUrl);
                }
                else
                {   
                    options.inJustDecodeBounds = false;

                    connection = (HttpURLConnection)url.openConnection();

                    stream = connection.getInputStream();

                    image = BitmapFactory.decodeStream(stream, null, options);

                    return image;
                }
            }

            catch(Exception e)
            {
                Log.e("getImage", e.toString());
            }
        }

        return image;
    }

ШАГ 3: Использование от Activity или Adapter

Примечание: Если вы хотите загрузить изображение из URL из Activity Класс. Используйте второй конструктор DownloadImageTask, но если вы хотите отобразить изображение из Adapter, используйте первый конструктор DownloadImageTask (например, у вас есть изображение в ListView, и вы устанавливаете изображение из 'Adapter')

ИСПОЛЬЗОВАНИЕ ОТ ДЕЯТЕЛЬНОСТИ:

ImageView imv = (ImageView) findViewById(R.id.imageView);
ImagesCache cache = ImagesCache.getInstance();//Singleton instance handled in ImagesCache class.
cache.initializeCache();

String img = "your_image_url_here";

Bitmap bm = cache.getImageFromWarehouse(img);

if(bm != null)
{
  imv.setImageBitmap(bm);
}
else
{
  imv.setImageBitmap(null);

  DownloadImageTask imgTask = new DownloadImageTask(cache, imv, 300, 300);//Since you are using it from `Activity` call second Constructor.

  imgTask.execute(img);
}

ИСПОЛЬЗОВАНИЕ ИЗ АДАПТЕРА:

ImageView imv = (ImageView) rowView.findViewById(R.id.imageView);
ImagesCache cache = ImagesCache.getInstance();
cache.initializeCache();

String img = "your_image_url_here";

Bitmap bm = cache.getImageFromWarehouse(img);

if(bm != null)
{
  imv.setImageBitmap(bm);
}
else
{
  imv.setImageBitmap(null);

  DownloadImageTask imgTask = new DownloadImageTask(this, 300, 300);//Since you are using it from `Adapter` call first Constructor.

  imgTask.execute(img);
}

Примечание:

cache.initializeCache() вы можете использовать это утверждение в самой первой Деятельности вашего приложения. После того, как вы инициализировали кеш, вам никогда не нужно будет инициализировать его каждый раз, если вы используете ImagesCache instance.

Я никогда не разбираюсь в объяснениях, но надеюсь, что это поможет новичкам понять, как кешировать, используя LruCache и его использование :)

EDIT:

В наши дни существуют очень известные библиотеки, известные как Picasso и Glide, которые можно использовать для очень эффективной загрузки изображений в приложении для Android. Попробуйте эту очень простую и полезную библиотеку Picasso для Android и Glide For Android . Вам не нужно беспокоиться о кэше изображений.

Picasso позволяет без проблем загружать изображения в ваше приложение - часто в одной строке кода!

Glide, как и Picasso, может загружать и отображать изображения из многих источники, а также заботиться о кэшировании и сохранении памяти влияние при выполнении манипуляций с изображением. Он был использован официальным Приложения Google (например, приложение для Google I / O 2015) и столь же популярны как Пикассо. В этой серии мы рассмотрим различия и Преимущества Glide перед Пикассо.

Вы также можете посетить блог для разницы между Glide и Picasso

17 голосов
/ 22 декабря 2009

Чтобы загрузить изображение и сохранить его на карту памяти, вы можете сделать это следующим образом.

//First create a new URL object 
URL url = new URL("http://www.google.co.uk/logos/holiday09_2.gif")

//Next create a file, the example below will save to the SDCARD using JPEG format
File file = new File("/sdcard/example.jpg");

//Next create a Bitmap object and download the image to bitmap
Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());

//Finally compress the bitmap, saving to the file previously created
bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream(file));

Не забудьте добавить интернет-разрешение в манифест:

<uses-permission android:name="android.permission.INTERNET" />
13 голосов
/ 02 октября 2010

Я хотел бы рассмотреть возможность использования кеша изображений droidfu. Он реализует как кэш-память в памяти, так и на диске. Вы также получаете WebImageView, в котором используется библиотека ImageCache.

Вот полное описание droidfu и WebImageView: http://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryadapter/

9 голосов
/ 28 февраля 2012

Как предложил Thunder Rabbit, ImageDownloader - лучший вариант для этой работы. Я также нашел небольшое изменение класса в:

http://theandroidcoder.com/utilities/android-image-download-and-caching/

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

9 голосов
/ 28 января 2011

Я пробовал SoftReferences, они слишком агрессивно исправлены в Android, и я чувствовал, что нет смысла их использовать

7 голосов
/ 11 июля 2012

В официальном разделе обучения Android есть специальная запись об этом: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html

Раздел совершенно новый, его не было, когда задавался вопрос.

Предлагаемое решение - использовать LruCache. Этот класс был представлен в Honeycomb, но также включен в библиотеку совместимости.

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

Пример кода с официальной страницы:

private LruCache mMemoryCache;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // Get memory class of this device, exceeding this amount will throw an
    // OutOfMemory exception.
    final int memClass = ((ActivityManager) context.getSystemService(
            Context.ACTIVITY_SERVICE)).getMemoryClass();

    // Use 1/8th of the available memory for this memory cache.
    final int cacheSize = 1024 * 1024 * memClass / 8;

    mMemoryCache = new LruCache(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            // The cache size will be measured in bytes rather than number of items.
            return bitmap.getByteCount();
        }
    };
    ...
}

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, bitmap);
    }
}

public Bitmap getBitmapFromMemCache(String key) {
    return mMemoryCache.get(key);
}

Ранее SoftReferences были хорошей альтернативой, но не больше, цитируя с официальной страницы:

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

7 голосов
/ 11 июня 2011

Это хороший улов Джо. В приведенном выше примере кода есть две проблемы - одна - объект ответа не является экземпляром Bitmap (когда мой URL ссылается на jpg, например http: \ website.com \ image.jpg, это

org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl $ LimitedInputStream).

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

http://codebycoffee.com/2010/06/29/using-responsecache-in-an-android-app/

API кэширования URLConnection описывается здесь:

http://download.oracle.com/javase/6/docs/technotes/guides/net/http-cache.html

Я все еще думаю, что это правильное решение, чтобы пойти по этому пути, но вам все еще нужно написать кеш. Звучит весело, но я бы предпочел написать особенности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...