一、使用内存缓存,通过使用LruCache类,需要确定缓存的大小,如使用app运行内存的1/8:
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;
}
};
...
}
Bitmap存入缓存(调用put()方法):
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}从缓存中读出Bitmap(调用get()方法):
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
注:在常见的hpdi设备上,程序的运行内存的1/8通常为4MB,一幅全屏800*480分辨率的图片大约要1.5MB空间,
所以该缓存可以存储大约2.5张图片。在加载图片时,先检测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);
}
}二、使用磁盘缓存
使用内存缓存有不小的局限性,如当你的进程因为系统运行内存不够被杀死时,缓存的文件就被破坏了,
这样下次又需要重新加载,可以通过磁盘进行缓存。以下实例使用了开源项目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;
}
...
}
private void addBitmapToDiskCache(String key, Bitmap bitmap) {
synchronized (mDiskCacheLock) {
if (mDiskCache != null) {
DiskLruCache.Editor editor = null;
try {
editor = mDiskCache.edit(key);
if (editor != null) {
OutputStream stream = editor.newOutputStream(0);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = baos.toByteArray();
stream.write(byteArray);
editor.commit();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private Bitmap getBitmapFromDiskCache(String key) {
synchronized (mDiskCacheLock) {
//如果磁盘缓存初始化未结束,则等待结束
while (mDiskCacheStarting) {
try {
mDiskCacheLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (mDiskCache != null) {
try {
DiskLruCache.Snapshot snapshot = mDiskCache.get(key);
if (snapshot != null) {
InputStream inputStream = snapshot.getInputStream(0);
return BitmapFactory.decodeStream(inputStream);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
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);}
注:由于初始化缓存是在子线程中进行,这样有可能用户读取缓存文件时初始化还没完成,因此给初始化过程加锁;
磁盘操作不应该在主线程中进行。
三、处理程序运行时配置改变:
当用户转动屏幕时,界面会被重新创建,因此缓存对象被销毁,此时需要重新处理缓存文件。因此为了给用户一个流畅的使用体验,
可以给该界面保留一个Fragment,通过该Fragment把缓存对象传递到新的界面实例,这样你的界面就可以重新访问内存中的缓存:
private LruCache<String, Bitmap> mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
RetainFragment retainFragment =
RetainFragment.findOrCreateRetainFragment(getFragmentManager());
mMemoryCache = retainFragment.mRetainedCache;
if (mMemoryCache == null) {
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
... // Initialize cache here as usual
}
retainFragment.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();
fm.beginTransaction().add(fragment, TAG).commit();
}
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
}注:记得在Fragment调用setRetainInstance()方法,如上述的onCreate()方法。
本文介绍如何使用内存缓存和磁盘缓存来提高图片加载效率,包括LruCache的使用和DiskLruCache的实现,并讨论了如何处理配置变化。
442

被折叠的 条评论
为什么被折叠?



