Android圖像緩存


Answers

關於上面的優雅的connection.setUseCaches解決方案:不幸的是,沒有一些額外的努力,它不會工作。 您將需要使用ResponseCache.setDefault安裝ResponseCache.setDefault 。 否則, HttpURLConnection將默默忽略setUseCaches(true)位。

有關詳細信息,請參閱FileResponseCache.java頂部的FileResponseCache.java

http://libs-for-android.googlecode.com/svn/reference/com/google/android/filecache/FileResponseCache.html

(我會在評論中發帖,但我顯然沒有足夠的SO業力。)

Question

如何在從網絡下載圖像後緩存圖像?







正如雷兔建議的那樣,ImageDownloader是最適合這項工作的人。 我還發現這個班稍微有些變化:

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

兩者之間的主要區別在於ImageDownloader使用Android緩存系統,修改後的系統使用內部和外部存儲器作為緩存,無限期地保留緩存的圖像,或者直到用戶手動刪除緩存的圖像。 作者還提到了Android 2.1的兼容性。




使用LruCache高效地緩存圖像。 您可以從Android開發人員網站了解有關LruCache信息

我已經使用下面的解決方案在Android中下載和緩存圖片。 您可以按照以下步驟操作:

第1 ImagesCache 製作Class Named ImagesCache 。 我Singleton object for this class使用了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步:使用您的ActivityAdapter

注意:如果你想從Activity類的URL加載圖像。 使用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()可以在應用程序的第一個Activity中使用此語句。 初始化緩存後,如果您使用ImagesCache實例,則永遠不需要每次初始化它。

我從來沒有擅長解釋的東西,但希望這將有助於初學者如何使用LruCache緩存和它的用法:)

編輯:

現在有一天,有非常著名的圖書館被稱為PicassoGlide ,可以用來在android應用程序中非常高效地加載圖像。 試試這個非常簡單和有用的圖書館畢加索的androidGlide For Android 。 您不必擔心緩存圖像。

畢加索允許在應用程序中輕鬆加載圖像 - 通常只需一行代碼!

滑動,就像畢加索一樣,可以加載和顯示來自多個來源的圖像,同時在進行圖像操作時也可以緩存緩存並保持較低的內存影響。 它已被官方Google應用程序(例如Google I / O 2015應用程序)使用,並且與Picasso一樣受歡迎。 在本系列中,我們將探索滑翔畢加索的差異和優勢。

您還可以訪問博客,了解Glide和畢加索之間差異




有關Android的官方培訓部分有一個特殊的條目: http : //developer.android.com/training/displaying-bitmaps/cache-bitmap.html

這個部分是相當新的,當問題提出時,它不在那裡。

建議的解決方案是使用LruCache。 該類是在Honeycomb中引入的,但它也包含在兼容性庫中。

您可以通過設置最大數量或條目來初始化LruCache,並且會在您超出限制時自動對您進行分類並清理較少使用的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 Level 11)之前,位圖的支持數據存儲在本地存儲器中,而不是以可預測的方式發布,這可能會導致應用程序暫時超出其內存限制並崩潰。







我一直在摔跤一段時間; 使用SoftReferences的答案會過快地丟失數據。 建議實例化RequestCache的答案太亂了,再加上我永遠找不到完整的示例。

ImageDownloader.java對我來說非常ImageDownloader.java 。 它使用HashMap,直到達到容量或清除超時發生,然後將事物移動到SoftReference,從而使用兩全其美。




實際上我的工作是在我的Main類上設置ResponseCache:

try {
   File httpCacheDir = new File(getApplicationContext().getCacheDir(), "http");
   long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
   HttpResponseCache.install(httpCacheDir, httpCacheSize);
} catch (IOException e) { } 

connection.setUseCaches(true);

下載位圖時。

http://practicaldroid.blogspot.com/2013/01/utilizing-http-response-cache.html






Links