在ListView和GridView中,如果子View在屏幕中看不到,對應的子View的資源會被自動的釋放,如果子View重新在屏幕中顯示,就需要去重新加載資源,如果是從網絡或者從硬盤中去讀取資源,這樣導致的後果是滑動的時候,顯示的效果不是很流暢,如果能將之前已經加載過的資源 放在緩存中,每次都是從緩存中讀取資源,這樣顯示的效果就會變得很流暢,下面分別介紹兩種緩存的形式,內存緩存和硬盤緩存
內存緩存
private LruCache<String, Bitmap> mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
//獲得虛擬機的內存,如果超出這個容量就會報出OutOfMemory exception
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
//使用內存的1/8作爲bitmap的緩存
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);
}
當ImageView要顯示時,首先判斷緩存中是否已經有對應的資源,如果有,就馬上顯示,沒有的話,纔會在後臺去加載對應的資源
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> {
private final WeakReference<ImageView> imageViewReference;
private int data = 0;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
// 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;
}
// Once complete, see if ImageView is still around and set bitmap.
@Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask =
getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask && imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
在後臺加載圖片時是將圖片放在緩存中
硬盤內存
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) {
...
// 初始化緩存
...
// 在後臺線程中初始化緩存
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; // 初始化是否完成
mDiskCacheLock.notifyAll(); // 通知其他的等待線程
}
return null;
}
}
//加載Bitmap的後臺線程
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
// 在後臺解析bitmap.
@Override
protected Bitmap doInBackground(Integer... params) {
final String imageKey = String.valueOf(params[0]);
// 檢查緩存中的資源
Bitmap bitmap = getBitmapFromDiskCache(imageKey);
if (bitmap == null) { // 沒有資源
// 處理bitamp
final Bitmap bitmap = decodeSampledBitmapFromResource(
getResources(), params[0], 100, 100));
}
// 加載bitmap到緩存中
addBitmapToCache(imageKey, bitmap);
return bitmap;
}
...
}
public void addBitmapToCache(String key, Bitmap bitmap) {
// 在內存緩存中添加資源
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
// 同樣在disk緩存中也添加對應的資源
synchronized (mDiskCacheLock) {
if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {
mDiskLruCache.put(key, bitmap);
}
}
}
//總緩存中獲得資源
public Bitmap getBitmapFromDiskCache(String key) {
synchronized (mDiskCacheLock) {
// 如果diskCache在後臺運行,則等待
while (mDiskCacheStarting) {
try {
mDiskCacheLock.wait();
} catch (InterruptedException e) {}
}
if (mDiskLruCache != null) {
return mDiskLruCache.get(key);
}
}
return null;
}
//創建一個唯一的文件夾作爲緩存區,如果有外置的sdcard,就使用,否則就使用內部的sdcard
public static File getDiskCacheDir(Context context, String uniqueName) {
//檢查外置sdcard的狀態
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線程中作讀寫硬盤的操作,最終處理完成的bitmap會分別加載到內存緩存和硬盤緩存中處理配置的變化
private LruCache<String, Bitmap> mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
RetainFragment mRetainFragment =
RetainFragment.findOrCreateRetainFragment(getFragmentManager());
mMemoryCache = RetainFragment.mRetainedCache;
if (mMemoryCache == null) {
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
... // Initialize cache here as usual
}
mRetainFragment.mRetainedCache = mMemoryCache;
}
...
}
class RetainFragment extends Fragment {
private static final String TAG = "RetainFragment";
public LruCache<String, Bitmap> mRetainedCache;
public RetainFragment() {}
public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
if (fragment == null) {
fragment = new RetainFragment();
}
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
}