緩存圖片

原文地址:http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html html

緩存圖片java

        加載一個圖片到界面中是很簡單的,可是若是一次性要加載一堆大的圖片就變的複雜多了。在許多狀況下(好比使用ListView、GridView或是ViewPager組件)界面上能顯示的圖片隨着滾動會無限制的增長。android

        緩存的使用須要被控制當組件在不斷的重複使用的時候。若是你不長時間引用加載來的圖片的話,垃圾蒐集器也會清除他。爲了流暢快速的加載界面,不但願組件每次顯示的時候都從新加載一次圖片。利用緩存和磁盤緩存就可以快速加載圖片。這一課就是介紹若是經過緩存和磁盤緩存快速加載多張圖片。
緩存

使用內存緩存app

內存緩存經過佔用應用固定的內存達到快速讀取緩存。LruCache類能很好的解決圖片緩存佔用問題,保證最近使用的資源強引用在LinkedHashMap中,刪除最近最少使用的資源。
ide

過去一種很是流行的緩存實現是經過SoftReference或是WeakReference存儲圖片緩存,然而這種方法再也不推薦。從2.3開始垃圾收集器會更加積極的蒐集soft/weak的應用這將使得他們無效了。在3.0之後,返回的圖片數據存儲在一個私有的內存中,以一種不可預見的方式釋放,可能致使應用暫時超過其內存限制而奔潰。ui

爲了給LruCache選中一個適合的大小,有一些因素須要考慮:
this

一、應用剩下多少的內存spa

二、屏幕一次要展現多少圖片,須要多少圖片準備用於屏幕展現線程

三、設備的屏幕大小和分辨率是多少,對於高分辨率設別(xhdpi)像Galaxy Nexus比hdpi的設備須要更大的緩存來存儲圖片

四、圖片的分辨率和配置和其佔用的緩存

五、圖片存儲是否頻繁,是否有其餘更頻繁的,若是有要確保其內存,甚至可使用多個LruCache對象處理不一樣組的圖片

六、可否平衡質量和數量,大多數狀況下存儲更多低質量的圖片比存儲高質量的圖片來的重要。

對於全部應用來講沒有專門的大小或公式。取決於你使用的解決方案。緩存過小會致使而外開銷,緩存太大會致使內存溢出。

下面是一個設置圖片LruCache緩存的例子:

private LruCache<String, Bitmap> mMemoryCache;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // Get max available VM memory, exceeding this amount will throw an
    // OutOfMemory exception. Stored in kilobytes as LruCache takes an
    // int in its constructor.
    final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

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

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

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

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

在這個例子中,應用1/8的內存分配給緩存,一個普通的hdpi設備的最小緩存在4MB左右(32/8),一個全屏的GridView在800x480的設備中大概佔用1.5MB(800x480x4bytes),所以能緩存的最少頁面的2.5個頁面的緩存。當加載圖片在ImageView中時先從LruCache中獲取

public void loadBitmap(int resId, ImageView imageView) {
    final String imageKey = String.valueOf(resId);

    final Bitmap bitmap = getBitmapFromMemCache(imageKey);
    if (bitmap != null) {
        mImageView.setImageBitmap(bitmap);
    } else {
        mImageView.setImageResource(R.drawable.image_placeholder);
        BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
        task.execute(resId);
    }
}

BitmapWorkerTask須要更新到緩存中

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    ...
    // Decode image in background.
    @Override
    protected Bitmap doInBackground(Integer... params) {
        final Bitmap bitmap = decodeSampledBitmapFromResource(
                getResources(), params[0], 100, 100));
        addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
        return bitmap;
    }
    ...
}

使用磁盤緩存

內存的使用對於控件快速加載圖片來講是很是有用的,然而不能期望全部圖片都存儲在內存中,像GridView這樣的組件加載大量的數據可以很輕鬆的佔滿緩存。應用會被其餘任務所打斷好比電話,當其在後臺時,有可能被殺死,緩存被銷燬。一旦返回應用時,又必須從新加載。

這種狀況下使用磁盤緩存加載再也不內存中的圖片能減小加載時間。固然從磁盤獲取圖片是緩慢的不肯定的,須要在後臺線程中執行。

ContentProvider多是一個更合適的地方來存儲緩存圖片若是他們更頻繁地訪問,例如在一個圖片庫應用程序。

下面是使用DiskLruCache緩存的例子:

private DiskLruCache mDiskLruCache;
private final Object mDiskCacheLock = new Object();
private boolean mDiskCacheStarting = true;
private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
private static final String DISK_CACHE_SUBDIR = "thumbnails";

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // Initialize memory cache
    ...
    // Initialize disk cache on background thread
    File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);
    new InitDiskCacheTask().execute(cacheDir);
    ...
}

class InitDiskCacheTask extends AsyncTask<File, Void, Void> {
    @Override
    protected Void doInBackground(File... params) {
        synchronized (mDiskCacheLock) {
            File cacheDir = params[0];
            mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);
            mDiskCacheStarting = false; // Finished initialization
            mDiskCacheLock.notifyAll(); // Wake any waiting threads
        }
        return null;
    }
}

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    ...
    // Decode image in background.
    @Override
    protected Bitmap doInBackground(Integer... params) {
        final String imageKey = String.valueOf(params[0]);

        // Check disk cache in background thread
        Bitmap bitmap = getBitmapFromDiskCache(imageKey);

        if (bitmap == null) { // Not found in disk cache
            // Process as normal
            final Bitmap bitmap = decodeSampledBitmapFromResource(
                    getResources(), params[0], 100, 100));
        }

        // Add final bitmap to caches
        addBitmapToCache(imageKey, bitmap);

        return bitmap;
    }
    ...
}

public void addBitmapToCache(String key, Bitmap bitmap) {
    // Add to memory cache as before
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, bitmap);
    }

    // Also add to disk cache
    synchronized (mDiskCacheLock) {
        if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {
            mDiskLruCache.put(key, bitmap);
        }
    }
}

public Bitmap getBitmapFromDiskCache(String key) {
    synchronized (mDiskCacheLock) {
        // Wait while disk cache is started from background thread
        while (mDiskCacheStarting) {
            try {
                mDiskCacheLock.wait();
            } catch (InterruptedException e) {}
        }
        if (mDiskLruCache != null) {
            return mDiskLruCache.get(key);
        }
    }
    return null;
}

// Creates a unique subdirectory of the designated app cache directory. Tries to use external
// but if not mounted, falls back on internal storage.
public static File getDiskCacheDir(Context context, String uniqueName) {
    // Check if media is mounted or storage is built-in, if so, try and use external cache dir
    // otherwise use internal cache dir
    final String cachePath =
            Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
                    !isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :
                            context.getCacheDir().getPath();

    return new File(cachePath + File.separator + uniqueName);
}

即便是初始化磁盤緩存的任務都不該該佔用UI線程。但並不意味着讀取緩存會在初始化以前,經過鎖來控制其餘任務須要在初始化完成後才能執行。

內存的獲取在UI線程,磁盤的獲取在後臺線程,磁盤的操做不能佔用UI線程,當圖片加載完成後,圖片會存儲在內存和磁盤中供之後使用。

相關文章
相關標籤/搜索