用戶在使用ListView或GridView時,控件會自動把用戶滑過的已不在當前顯示區域的ChildView回收掉,固然也會把該子視圖上的bitmap回收掉以釋放內存,所以,爲了保證一個流暢,快速的操做體驗,咱們應當避免反覆的對同一張圖片進行加載,好比說用戶在往下看圖的過程當中又向上滑回去看圖,這時對於已經上面已經加載過的圖片咱們就沒有必要讓它再加載一遍了,應該能很快的把圖片顯示出來,這裏咱們要使用緩存來達到這一目的。
html
一,使用Memory Cache:緩存
內存緩存速度快,同時爲了更加適應實際應用的場景,咱們使用LruCache來達到按使用頻率緩存的目的,把最近使用的加入緩存,較長時間不用的則會剔除掉釋放出空間。app
緩存的代碼以下:ide
- 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);
- }
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);
}
那麼咱們在loadBitmap的時候就能夠先檢查下緩存中保存的是否有該圖片,有則直接取出使用,再也不進行加載。ui
新的代碼以下:this
- 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);
- }
- }
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);
}
}
固然,咱們也要在加載圖片是及時的維護緩存,把剛使用到的圖片add進緩存中去。spa
新的代碼以下:.net
- 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;
- }
- ...
- }
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;
}
...
}
在使用內存作緩存的基礎上,咱們還可使用Disk控件作爲緩存,構成一種二級緩存的結構,設想這種狀況,若是App在使用的過程被忽然來電打斷,那麼此時有可能就會引發系統內存的回收,當用戶再次切換到App時,App就要進行次很明顯的圖片再次加載的過程。這個時候,咱們就須要用到Disk了,由於足夠持久。code
下面是是原來的基礎上增長使用Disk Cache 的例子:orm
- 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);
- }