资源描述
基于Volley和DiskLruCache的缓存策略
在我们开发应用过程中,不可避免的使用网络图片,那么图片请求和缓存就成了一个问题,本着为用户节省流量的角度,我们的应用就应该是已经下载的图片不会重复下载,以及下载过的图片没有网络也能查看的效果,这里就涉及到了网络请求、内存缓存和磁盘缓存。
简单的介绍一下今天的两位主角:
DiskLruCache :是基于Lru磁盘缓存的Java实现。
Volley :Google推出的Android异步网络请求框架和图片加载框架(自备梯子)。
细心的小伙伴应该发现咋是两个主角呢,说好的内存缓存呢?其实在Volley中已经默认实现内存缓存了,我们简单的介绍下Volley的接口及关键函数(以下为引用)。
ImageLoader
ImageLoader类需要一个请求的实例以及一个ImageCache类的实现。图片通过传递一个URL和一个ImageListener实例到get()方法进行加载。从那里,ImageLoader检查ImageCache,如果图像不是在缓存中,就从网络中加载图片。
NetworkImageView
这个类代替布局中的ImageViews类,而且需要使用ImageLoader类。NetworkImageView类的setUrl()方法需要一个URL路径字符串和一个ImageLoader实例。然后使用ImageLoder的get()方法来获取图片数据。
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/twitterUserImage"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="5dp" />
ImageCache
Volley库的ImageCache接口允许你使用你喜好的L1缓存实现。
ImageCache接口有两个方法,getBitmap(String url)和putBitmap(String url, Bitmap bitmap)。这些桩是非常简单的,它们可以添加到任何缓存实现里。
有两种可用的缓存实现。推荐的方法是在内存中使用一个基础的LRU缓存。对于硬盘的缓存实现,我选择使用Jack Wharton编写的DiskLruCache。我选择这个实现是因为它在Android社区中被频繁的使用。使用一个基于磁盘的L1缓存可能导致阻塞I/O问题。Volley已经有一个隐式的硬盘L2缓存。硬盘L1缓存已经被包含在内了。
原生项目的介绍如下
以下是这个实现的主要组件:
RequestManager
RequestManager维护我们的RequestQueue的一个引用。Volley使用ResuestQueue来处理我们对Twitter数据和图片加载的请求。
GsonRequest
GsonRequest与图片加载没有直接联系,但它代表了如何扩展Volley请求类来处理JSON解析。该类用于对Twitter的GET请求和TwitterData对象的结果。
BitmapLruImageCache
该类是一个基本的“最近最少使用(LRU)”内存缓存实现。它速度快但不会阻塞I/O。这是推荐的方法。
DiskLruImageCache
DiskLruImageCache是一个位图版本的DiskLruCache封装。它从DiskLruCache中增加并检索位图,还处理缓存的实例化。硬盘缓存可能会造成I/O阻塞。
ImageCacheManager
ImageCacheManager持有ImageCache和Volley ImageLoader引用。
在ImageCacheManager中,你可能注意到一点就是我们使用URL字符串的hashCode()作为缓存的键值。这是由于URL中的某些字符不能作为缓存的键值。
BuzzArrayAdapter
该适配器比较简单的。这里只需要注意一点,我们要实现Volley的Listener和ErrorListener接口,并且把该适配器作为Listener参数传递给了NetworkImageView 的 setUrl(String string , Listener listener, ErrorListener errorListener) 方法。这个适配器包含了一些额外代码用于滚动时加载旧的tweets。
以上就是原博的内容,我们的改造是基于这个项目来进行的。
首先来介绍下具体的思路,如图:
简单介绍一下思路:加载图片使用的是NetworkImageView的setImageUrl()方法,参数是图片的下载地址URL和ImageLoader实例,当URL不为空的时候会读取内存缓存(BitmapLruImageCache)中对应URL的图片,当读取不到时,去磁盘缓存(DiskLruImageCache)中查找,此处查找使用URL的hashCode值作为键值,如果找到图片则返回给内存并添加到内存保存,如果没有找到,返回NULL,Volley会自动启动下载图片的请求并添加到队列中,在下载完成后,分别将图片保存到对应的内存缓存和磁盘缓存中。
1.首先改造的是项目中缓存的实现,需要修改为磁盘和内存同时使用。
在ImageCacheManager中删除CacheType的两种枚举类型,并添加磁盘缓存的实现对象,然后修改实例化的方法,如下:
/**
* Image cache implementation
*/
private BitmapLruImageCache mMemoryCache;
/**
* Volley image loader
*/
private ImageLoader mImageLoader;
/**
* Image disk cache implementation
*/
private DiskLruImageCache mImageCache;
/**
* Initializer for the manager. Must be called prior to use.
*
* @param context
* application context
* @param uniqueName
* name for the cache location
* @param cacheSize
* max size for the cache
* @param compressFormat
* file type compression format.
* @param quality
*/
public void init(Context context, String uniqueName, int cacheSize, CompressFormat compressFormat,
int quality){
mImageCache= new DiskLruImageCache(context, uniqueName, cacheSize, compressFormat, quality);
mMemoryCache = new BitmapLruImageCache(cacheSize);
mImageLoader = new ImageLoader(RequestManager.getRequestQueue(), mMemoryCache);
}
需要说明的是,这里可以看成只增加了DiskLruImageCache的实例,Volley原生的使用内存加载请求图片的逻辑方法并没有改变,所以你如果想对自己现有的项目修改,则只需添加DiskLruImageCache的实例即可。
2.修改MainApplication中初始化的方法
/**
* Create the image cache. Uses Memory Cache by default. Change to Disk for a Disk based LRU implementation.
*/
private void createImageCache(){
ImageCacheManager.getInstance().init(this,
"pic"
, DISK_IMAGECACHE_SIZE
, DISK_IMAGECACHE_COMPRESS_FORMAT
, DISK_IMAGECACHE_QUALITY);
}
去掉了最后一个缓存类型的参数,并修改了图片保存的目录为pic(此处根据你项目的实际情况而定),在测试过程中发现磁盘缓存保存的地址不够友好,改写了DiskLruImageCache类中getDiskCacheDir的方法,如下:
/**
* Get a usable cache directory (external if available, internal otherwise).
* 根据传入的uniqueName获取硬盘缓存的路径地址。
* @param context
* The context to use
* @param uniqueName
* A unique directory name to append to the cache dir
* @return The cache dir
*/
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
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
return new File(cachePath + File.separator + uniqueName);
}
3. 修改获取和添加图片的逻辑
上面的准备工作做完,我们就只差一步就成功了。
上面也说道,这次修改没有改变Volley原生加载图片的方法,所以我们只需要在获取和添加图片的地方做些小改动就可以,这里默认使用的是内存缓存图片的方法,所以修改的是BitmapLruImageCache类,如下:
@Override
public Bitmap getBitmap(String url) {
Log.v(TAG, "Retrieved item from Mem Cache");
Bitmap bitmap = get(url);
//如果没有在内存中找到则去磁盘缓存查找
if(bitmap==null){
bitmap = ImageCacheManager.getInstance().getBitmap(url);
//如果磁盘缓存找到,添加到内存缓存中
if(bitmap!=null){
putBitmap(url,bitmap);
}
}
return bitmap;
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
//在getBitmap方法返回NULL时,Volley启动下载图片的任务下载图片成功后会调用此方法
Log.v(TAG, "Added item to Mem Cache");
//原生的内存缓存添加图片到内存中的方法
put(url, bitmap);
//将图片同时添加到磁盘缓存中
ImageCacheManager.getInstance().putBitmap(url,bitmap);
}
以上就是全部的修改啦,很简单吧。
GitHub项目里面的例子是下载Tweets数据的,可以修改为你自己项目的数据,学以致用,赶紧去实践吧。
参考:
Google I/O 2013: Volley Image Cache Tutorial
Google I/O 2013: Volley加载图片添加缓存处理
GitHub项目
展开阅读全文