Android 基于Vollery.ImageLoader的拓展(替换缓存策略,增加本地持久化)

来源:互联网 发布:mysql 字符串转日期 编辑:程序博客网 时间:2024/05/07 19:43

没有了解过Vollery的朋友,请自行google了解.这里不做复述.

先来看ImageLoader的关键源码:

public ImageContainer get(String requestUrl, ImageListener imageListener,int maxWidth, int maxHeight) {// only fulfill requests that were initiated from the main thread.throwIfNotOnMainThread();final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);// Try to look up the request in the cache of remote images.Bitmap cachedBitmap = mCache.getBitmap(cacheKey);if (cachedBitmap != null) {// Return the cached bitmap.ImageContainer container = new ImageContainer(cachedBitmap,requestUrl, null, null);imageListener.onResponse(container, true);return container;}// The bitmap did not exist in the cache, fetch it!ImageContainer imageContainer = new ImageContainer(null, requestUrl,cacheKey, imageListener);// Update the caller to let them know that they should use the default// bitmap.imageListener.onResponse(imageContainer, true);// Check to see if a request is already in-flight.BatchedImageRequest request = mInFlightRequests.get(cacheKey);if (request != null) {// If it is, add this request to the list of listeners.request.addContainer(imageContainer);return imageContainer;}// The request is not already in flight. Send the new request to the// network and// track it.Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth,maxHeight, cacheKey);mRequestQueue.add(newRequest);mInFlightRequests.put(cacheKey, new BatchedImageRequest(newRequest,imageContainer));return imageContainer;}
这个是请求图片的接口,可以看到,他通过图片路径+高度+宽度生成了一个缓存的Key.然后会通过这个Key去mCache中查找是否有缓存图片.如果有的话,会直接直接把图片包装成一个ImageContainer.且直接返回,如果没有找找到的话.会生成一个一个请求,将这条请求入队.执行策略的话,内部是一个优先级的阻塞队列.而ImageRequest的优先级是low.在ImageRequest这个类的getPriority()接口会返回优先级.注意的是,onResponse接口的第二参数,表示是否是及时返回的.如果为true时,一般表示图片在内存中,所以一般如果参数为True的话,不用启动动画效果.为false的话表示进行了网络请求.这时即可对imageview进行一些动画的过度.

protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth,int maxHeight, final String cacheKey) {return new ImageRequest(requestUrl, new Listener<Bitmap>() {@Overridepublic void onResponse(Bitmap response) {onGetImageSuccess(cacheKey, response);}}, maxWidth, maxHeight, Config.RGB_565, new ErrorListener() {@Overridepublic void onErrorResponse(VolleyError error) {onGetImageError(cacheKey, error);}});}
这个接口生产了一个ImageRequest对象.成功时会触发

protected void onGetImageSuccess(String cacheKey, Bitmap response) {// cache the image that was fetched.mCache.putBitmap(cacheKey, response);// remove the request from the list of in-flight requests.BatchedImageRequest request = mInFlightRequests.remove(cacheKey);if (request != null) {// Update the response bitmap.request.mResponseBitmap = response;// Send the batched responsebatchResponse(cacheKey, request);}}
失败时会触发

protected void onGetImageError(String cacheKey, VolleyError error) {// Notify the requesters that something failed via a null result.// Remove this request from the list of in-flight requests.BatchedImageRequest request = mInFlightRequests.remove(cacheKey);if (request != null) {// Set the error for this requestrequest.setError(error);// Send the batched responsebatchResponse(cacheKey, request);}}
可以重写这2个接口,进行本地的保存操作.ImageLoader的源码简单的介绍到这里.接下来看如何在原有基础上增加一些拓展功能.


package com.chediandian.core.image;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import android.graphics.Bitmap;import android.graphics.Bitmap.CompressFormat;import com.android.volley.RequestQueue;import com.android.volley.VolleyError;import com.android.volley.toolbox.ImageLoader;import com.chediandian.core.image.cache.LruBitmapCache;import com.chediandian.core.image.cache.local.name.LocalName;import com.chediandian.core.image.utils.XKImageUtils;/** * <p> * <b>ImageLoader增强版</b> * </p> * <li>支持本地缓存</li> <li>支持取消指定的图片请求</li> <li>比之前更强大的内存缓存池</li> *  * @version 1.0 * @since 1.0 * @author Shun * @hide 暂时不对外不暴露 *  */class XKImageLoader extends ImageLoader {private LruBitmapCache mCache;private LocalName mLocalName;private String mLocalDirsPath;private ExecutorService mPool;private Map<String, ImageContainer> mRequests;/** *  * @param queue * @param imageCache * @param localDirsPath *            本地缓存文件夹路径 * @param localName *            文件名策略接口 */public XKImageLoader(RequestQueue queue, ImageCache imageCache,String localDirsPath, LocalName localName) {super(queue, imageCache);// 这里记录引用,为了添加本地缓存mCache = (LruBitmapCache) imageCache;mLocalDirsPath = localDirsPath;mLocalName = localName;mPool = Executors.newFixedThreadPool(2);mRequests = new ConcurrentHashMap<String, ImageContainer>();}/** * 复写get方法,增加本地是否有缓存的逻辑 */@Overridepublic ImageContainer get(String requestUrl, ImageListener imageListener,int maxWidth, int maxHeight) {String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);// 不在缓存,则从本地找if (mCache.getBitmap(cacheKey) == null) {File file = new File(mLocalDirsPath,mLocalName.getFileName(cacheKey));// 如果缓存文件存在,那么加入到缓存if (file.exists()) {mPool.execute(new LoadLocalTask(cacheKey, file.getAbsolutePath(), requestUrl, imageListener,maxWidth, maxHeight));return null;}}ImageContainer container = super.get(requestUrl, imageListener,maxWidth, maxHeight);// 如果bitmap为空,表示容器中没有缓存,即把它添加到请求Map中if (null == container.getBitmap()) {mRequests.put(cacheKey, container);}return container;}/** * 加载本地图片 *  * @param path *            图片路径 * @param imageListener *            图片监听 * @param maxWidth *            最大宽度 * @param maxHeight *            最大高度 */public void loadImage(String path, ImageListener imageListener,int maxWidth, int maxHeight) {String key = getCacheKey(path, maxWidth, maxHeight);// 不在缓存,则从本地找if (mCache.getBitmap(key) == null) {File file = new File(path);// 如果缓存文件存在,那么加入到缓存if (file.exists()) {mPool.execute(new LoadLocalTask(key, file.getAbsolutePath(),path, imageListener, maxWidth, maxHeight));}} else {Bitmap bitmap = mCache.getBitmap(key);ImageContainer container = new ImageContainer(bitmap, path, null,null);imageListener.onResponse(container, true);}}/** * 复写onGetImageSuccess,增加线程池来管理持久化 */@Overrideprotected void onGetImageSuccess(String cacheKey, Bitmap response) {// 持久化图片缓存File file = new File(mLocalDirsPath, mLocalName.getFileName(cacheKey));if (!file.exists()) {mPool.execute(new LocalTask(file.getAbsolutePath(), response));}super.onGetImageSuccess(cacheKey, response);// 从请求Map中移除这条请求,因为它已经完成mRequests.remove(cacheKey);}@Overrideprotected void onGetImageError(String cacheKey, VolleyError error) {super.onGetImageError(cacheKey, error);// 从请求Map中移除这条请求,因为它已经完成mRequests.remove(cacheKey);}/** * <p> * <b>持久化图片到本地的Task</b> * </P> *  * @version 1.0 * @since 1.0 * @author Shun *  */private class LocalTask implements Runnable {private String mPath;private Bitmap mBitmap;public LocalTask(String path, Bitmap bitmap) {this.mPath = path;this.mBitmap = bitmap;}@Overridepublic void run() {FileOutputStream out = null;try {if (null != mBitmap) {out = new FileOutputStream(mPath);mBitmap.compress(CompressFormat.JPEG, 100, out);}} catch (FileNotFoundException e) {e.printStackTrace();} finally {if (out != null) {try {out.close();out = null;} catch (IOException e) {e.printStackTrace();}}}}}/** * 加载本地图片Task *  * @version 1.0 * @since 1.0 * @author Shun *  */private class LoadLocalTask implements Runnable {String mCacheKey;String mPath;String mUrl;ImageListener mImageListener;int mWidth;int mHeight;public LoadLocalTask(String cacheKey, String path, String url,ImageListener imageListener, int width, int height) {this.mCacheKey = cacheKey;this.mPath = path;this.mUrl = url;this.mImageListener = imageListener;this.mWidth = width;this.mHeight = height;}@Overridepublic void run() {Bitmap bitmap = XKImageUtils.loadBitmap(mPath, mWidth, mHeight);if (null != bitmap) {mCache.putBitmap(mCacheKey, bitmap);}// 这个Hanlder是父类的全局变量mHandler.postDelayed(new DeliverTask(bitmap, mUrl, mImageListener),100);}}/** * 转发到主线程的Task *  * @version 1.0 * @since 1.0 * @author Shun *  */private class DeliverTask implements Runnable {Bitmap mCacheBitmap;String mUrl;ImageListener mImageListener;public DeliverTask(Bitmap cacheBitmap, String url,ImageListener imageListener) {this.mCacheBitmap = cacheBitmap;this.mUrl = url;this.mImageListener = imageListener;}@Overridepublic void run() {ImageContainer container = new ImageContainer(mCacheBitmap, mUrl,null, null);if (null != mCacheBitmap)mImageListener.onResponse(container, true);elsemImageListener.onErrorResponse(null);}}/** *  * <p> * <b>取消所有图片请求</b> * </p> * 注意的是,此类接口对本地图片无效 */public void cancelRequest() {for (String key : mRequests.keySet()) {ImageContainer container = mRequests.get(key);if (null != container)container.cancelRequest();}mRequests.clear();}/** *  * <p> * <b>取消指定的请求</b> * </p> *  * @param url *            请求的URL */public void cancelRequest(String url) {cancelRequest(url, 0, 0);}/** *  * <p> * <b>取消指定的请求</b> * </p> *  * @param url *            请求的URL * @param maxWidth *            加载时设置的宽度 * @param maxHeight *            加载时设置的高度 */public void cancelRequest(String url, int maxWidth, int maxHeight) {String key = getCacheKey(url, maxWidth, maxHeight);ImageContainer container = mRequests.remove(key);if (null != container) {container.cancelRequest();}}/** *  * <p> * <b>清除所有缓存</b> * </p> */public void clearCache() {if (null != mCache) {mCache.clear();}}/** *  * <p> * <b>清除指定缓存</b> * </p> *  * @param urlOrPath *            可以是Url可以是本地路径 */public boolean cearCache(String urlOrPath) {return clearCache(urlOrPath, 0, 0);}/** *  * <p> * <b>清除指定缓存</b> * </p> * 注意maxWidth,maxHeight必须与缓存请求时的大小保持一致 *  * @param urlOrPath *            可以是Url可以是本地路径 * @param maxWidth *            加载时设置的宽度 * @param maxHeight *            加载时设置的高度 */public boolean clearCache(String urlOrPath, int maxWidth, int maxHeight) {if (null != mCache) {Bitmap bitmap = mCache.remove(getCacheKey(urlOrPath, maxWidth,maxHeight));if (null != bitmap) {if (!bitmap.isRecycled()) {bitmap.recycle();}bitmap = null;return true;} else {return false;}}return false;}}
可以看到 我重写了onGetImageSuccess,onGetImageError,get 3个接口来实现本地化功能.大家可以看一下,注释先的很清楚,DeliverTask主要用于异步加载本地图片后调度到主线程.LocalTask主要用于图片的持久化.LoadLocalTask加载本地图片.需要注意的是mHandler. mHandler在源码中是私有的.我自己改成了受保护的.

get接口中,会先检查是否有缓存,没有缓存的话从本地找,如果本地有的话会讲一个任务放到线程池中去执行. 最后由DeliverTask 调度到主线程中 关键代码:

String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);// 不在缓存,则从本地找if (mCache.getBitmap(cacheKey) == null) {File file = new File(mLocalDirsPath,mLocalName.getFileName(cacheKey));// 如果缓存文件存在,那么加入到缓存if (file.exists()) {mPool.execute(new LoadLocalTask(cacheKey, file.getAbsolutePath(), requestUrl, imageListener,maxWidth, maxHeight));return null;}}
重写onGetImageSuccess的目的是为了在图片获取成功时将图片保存到本地 如果本地不存在,会执行一个持久化任务, 同样也是通过线程.

File file = new File(mLocalDirsPath, mLocalName.getFileName(cacheKey));if (!file.exists()) {mPool.execute(new LocalTask(file.getAbsolutePath(), response));}

至此,一个支持本地功能的ImageLoader完成,看注释应该很容易看懂代码,这里就不细讲了 :)



0 0