用户在使用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);
- }