教你写Android ImageLoader框架之图片缓存 (完结篇)

在教你写Android ImageLoader框架系列博文中,我们从基本架构到具体实现已经更新了大部分的内容。今天,我们来讲最后一个关键点,即图片的缓存。为了用户体验,通常情况下我们都会将已经下载的图片缓存起来,一般来说内存和本地都会有图片缓存。那既然是框架,必然需要有很好的定制性,这让我们又自然而然的想到了抽象。下面我们就一起来看看缓存的实现吧。

缓存接口

在教你写Android ImageLoader框架之图片加载与加载策略我们聊到了Loader,然后阐述了AbsLoader的基本逻辑,其中就有图片缓存。因此AbsLoader中必然含有缓存对象的引用。我们看看相关代码:

/** * @author mrsimple */{/*** 图片缓存*/private static BitmapCache mCache = SimpleImageLoader.getInstance().getConfig().bitmapCache;// 代码省略}

AbsLoader中定义了一个static的BitmapCache对象,这个就是图片缓存对象。那为什么是static呢?因为不管Loader有多少个,,缓存对象都应该是共享的,也就是缓存只有一份。说了那么多,那我们先来了解一下BitmapCache吧。

public interface BitmapCache {public Bitmap get(BitmapRequest key);(BitmapRequest key, Bitmap value);(BitmapRequest key);}

BitmapCache很简单,只声明了获取、添加、移除三个方法来操作图片缓存。这里有依赖了一个BitmapRequest类,这个类代表了一个图片加载请求,该类中有该请求对应的ImageView、图片uri、显示Config等属性。在缓存这块我们主要要使用图片的uri来检索缓存中是否含有该图片,缓存以图片的uri为key,Bitmap为value来关联存储。另外需要BitmapRequest的ImageView宽度和高度,以此来按尺寸加载图片。

定义BitmapCache接口还是为了可扩展性,面向接口的编程的理念又再一次的浮现在你面前。如果是你,你会作何设计呢?自己写代码来练习一下吧,看看自己作何考虑,如果实现,这样你才会从中有更深的领悟。

内存缓存

既然是框架,那就需要接受用户各种各样的需求。但通常来说框架会有一些默认的实现,对于图片缓存来说内存缓存就其中的一个默认实现,它会将已经加载的图片缓存到内存中,大大地提升图片重复加载的速度。内存缓存我们的策略是使用LRU算法,直接使用了support.v4中的LruCache类,相关代码如下。

/** * 图片的内存缓存,key为图片的uri,值为图片本身 * * @author mrsimple */{private LruCache<String, Bitmap> mMemeryCache;public MemoryCache() {maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);cacheSize = maxMemory / 4;mMemeryCache = new LruCache<String, Bitmap>(cacheSize) {(String key, Bitmap bitmap) {return bitmap.getRowBytes() * bitmap.getHeight() / 1024;}};}@Overridepublic Bitmap get(BitmapRequest key) {return mMemeryCache.get(key.imageUri);}(BitmapRequest key, Bitmap value) {mMemeryCache.put(key.imageUri, value);}(BitmapRequest key) {mMemeryCache.remove(key.imageUri);}}

就是简单的实现了BitmapCache接口,然后内部使用LruCache类实现内存缓存。比较简单,就不做说明了。

sd卡缓存

对于图片缓存,内存缓存是不够的,更多的需要是将图片缓存到sd卡中,这样用户在下次进入app时可以直接从本地加载图片,避免重复地从网络上读取图片数据,即耗流量,用户体验又不好。sd卡缓存我们使用了Jake Wharton的DiskLruCache类,我们的sd卡缓存类为DiskCache,代码如下 :

{/*** 1MB*/MB = 1024 * 1024;/*** cache dir*/String IMAGE_DISK_CACHE = “bitmap”;/*** Disk LRU Cache*/private DiskLruCache mDiskLruCache;/*** Disk Cache Instance*/private static DiskCache mDiskCache;/*** @param context*/private DiskCache(Context context) {initDiskCache(context);}public static DiskCache getDiskCache(Context context) {if (mDiskCache == null) {synchronized (DiskCache.class) {if (mDiskCache == null) {mDiskCache = new DiskCache(context);}}}return mDiskCache;}/*** 初始化sdcard缓存*/(Context context) {try {File cacheDir = getDiskCacheDir(context, IMAGE_DISK_CACHE);if (!cacheDir.exists()) {cacheDir.mkdirs();}mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 50 * MB);} catch (IOException e) {e.printStackTrace();}}/*** 获取sd缓存的目录,如果挂载了sd卡则使用sd卡缓存,否则使用应用的缓存目录。* @param context Context* @param uniqueName 缓存目录名,比如bitmap* @return*/public File getDiskCacheDir(Context context, String uniqueName) {String cachePath;if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {Log.d(“”, “### context : ” + context + “, dir = ” + context.getExternalCacheDir());cachePath = context.getExternalCacheDir().getPath();} else {cachePath = context.getCacheDir().getPath();}return new File(cachePath + File.separator + uniqueName);}Bitmap get(final BitmapRequest bean) {// 图片解析器BitmapDecoder decoder = new BitmapDecoder() {@Overridepublic Bitmap decodeBitmapWithOption(Options options) {final InputStream inputStream = getInputStream(bean.imageUriMd5);Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null,options);IOUtil.closeQuietly(inputStream);return bitmap;}};return decoder.decodeBitmap(bean.getImageViewWidth(),bean.getImageViewHeight());}private InputStream getInputStream(String md5) {Snapshot snapshot;try {snapshot = mDiskLruCache.get(md5);if (snapshot != null) {return snapshot.getInputStream(0);}} catch (IOException e) {e.printStackTrace();}return null;}(BitmapRequest key, Bitmap value) {// 代码省略 }(BitmapRequest key) {// 代码省略}}与其在那里苦苦挣扎,碍于面子硬撑,倒不如微笑着面对,

教你写Android ImageLoader框架之图片缓存 (完结篇)

相关文章:

你感兴趣的文章:

标签云: