volley中ImageLoader的理解和扩张

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

先来看ImageLoader的关键源码:

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

  1. protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth,  
  2.         int maxHeight, final String cacheKey) {  
  3.     return new ImageRequest(requestUrl, new Listener<Bitmap>() {  
  4.         @Override  
  5.         public void onResponse(Bitmap response) {  
  6.             onGetImageSuccess(cacheKey, response);  
  7.         }  
  8.     }, maxWidth, maxHeight, Config.RGB_565, new ErrorListener() {  
  9.         @Override  
  10.         public void onErrorResponse(VolleyError error) {  
  11.             onGetImageError(cacheKey, error);  
  12.         }  
  13.     });  
  14. }  
这个接口生产了一个ImageRequest对象.成功时会触发

  1. protected void onGetImageSuccess(String cacheKey, Bitmap response) {  
  2.     // cache the image that was fetched.  
  3.     mCache.putBitmap(cacheKey, response);  
  4.   
  5.     // remove the request from the list of in-flight requests.  
  6.     BatchedImageRequest request = mInFlightRequests.remove(cacheKey);  
  7.   
  8.     if (request != null) {  
  9.         // Update the response bitmap.  
  10.         request.mResponseBitmap = response;  
  11.   
  12.         // Send the batched response  
  13.         batchResponse(cacheKey, request);  
  14.     }  
  15. }  
失败时会触发

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


  1. package com.chediandian.core.image;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileNotFoundException;  
  5. import java.io.FileOutputStream;  
  6. import java.io.IOException;  
  7. import java.util.Map;  
  8. import java.util.concurrent.ConcurrentHashMap;  
  9. import java.util.concurrent.ExecutorService;  
  10. import java.util.concurrent.Executors;  
  11.   
  12. import android.graphics.Bitmap;  
  13. import android.graphics.Bitmap.CompressFormat;  
  14.   
  15. import com.android.volley.RequestQueue;  
  16. import com.android.volley.VolleyError;  
  17. import com.android.volley.toolbox.ImageLoader;  
  18. import com.chediandian.core.image.cache.LruBitmapCache;  
  19. import com.chediandian.core.image.cache.local.name.LocalName;  
  20. import com.chediandian.core.image.utils.XKImageUtils;  
  21.   
  22. /** 
  23.  * <p> 
  24.  * <b>ImageLoader增强版</b> 
  25.  * </p> 
  26.  * <li>支持本地缓存</li> <li>支持取消指定的图片请求</li> <li>比之前更强大的内存缓存池</li> 
  27.  *  
  28.  * @version 1.0 
  29.  * @since 1.0 
  30.  * @author Shun 
  31.  * @hide 暂时不对外不暴露 
  32.  *  
  33.  */  
  34. class XKImageLoader extends ImageLoader {  
  35.   
  36.     private LruBitmapCache mCache;  
  37.     private LocalName mLocalName;  
  38.     private String mLocalDirsPath;  
  39.     private ExecutorService mPool;  
  40.     private Map<String, ImageContainer> mRequests;  
  41.   
  42.     /** 
  43.      *  
  44.      * @param queue 
  45.      * @param imageCache 
  46.      * @param localDirsPath 
  47.      *            本地缓存文件夹路径 
  48.      * @param localName 
  49.      *            文件名策略接口 
  50.      */  
  51.     public XKImageLoader(RequestQueue queue, ImageCache imageCache,  
  52.             String localDirsPath, LocalName localName) {  
  53.         super(queue, imageCache);  
  54.         // 这里记录引用,为了添加本地缓存  
  55.         mCache = (LruBitmapCache) imageCache;  
  56.         mLocalDirsPath = localDirsPath;  
  57.         mLocalName = localName;  
  58.         mPool = Executors.newFixedThreadPool(2);  
  59.         mRequests = new ConcurrentHashMap<String, ImageContainer>();  
  60.     }  
  61.   
  62.     /** 
  63.      * 复写get方法,增加本地是否有缓存的逻辑 
  64.      */  
  65.     @Override  
  66.     public ImageContainer get(String requestUrl, ImageListener imageListener,  
  67.             int maxWidth, int maxHeight) {  
  68.         String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);  
  69.         // 不在缓存,则从本地找  
  70.         if (mCache.getBitmap(cacheKey) == null) {  
  71.             File file = new File(mLocalDirsPath,  
  72.                     mLocalName.getFileName(cacheKey));  
  73.             // 如果缓存文件存在,那么加入到缓存  
  74.             if (file.exists()) {  
  75.                 mPool.execute(new LoadLocalTask(cacheKey, file  
  76.                         .getAbsolutePath(), requestUrl, imageListener,  
  77.                         maxWidth, maxHeight));  
  78.                 return null;  
  79.             }  
  80.         }  
  81.         ImageContainer container = super.get(requestUrl, imageListener,  
  82.                 maxWidth, maxHeight);  
  83.         // 如果bitmap为空,表示容器中没有缓存,即把它添加到请求Map中  
  84.         if (null == container.getBitmap()) {  
  85.             mRequests.put(cacheKey, container);  
  86.         }  
  87.         return container;  
  88.     }  
  89.   
  90.     /** 
  91.      * 加载本地图片 
  92.      *  
  93.      * @param path 
  94.      *            图片路径 
  95.      * @param imageListener 
  96.      *            图片监听 
  97.      * @param maxWidth 
  98.      *            最大宽度 
  99.      * @param maxHeight 
  100.      *            最大高度 
  101.      */  
  102.     public void loadImage(String path, ImageListener imageListener,  
  103.             int maxWidth, int maxHeight) {  
  104.         String key = getCacheKey(path, maxWidth, maxHeight);  
  105.         // 不在缓存,则从本地找  
  106.         if (mCache.getBitmap(key) == null) {  
  107.             File file = new File(path);  
  108.             // 如果缓存文件存在,那么加入到缓存  
  109.             if (file.exists()) {  
  110.                 mPool.execute(new LoadLocalTask(key, file.getAbsolutePath(),  
  111.                         path, imageListener, maxWidth, maxHeight));  
  112.             }  
  113.         } else {  
  114.             Bitmap bitmap = mCache.getBitmap(key);  
  115.             ImageContainer container = new ImageContainer(bitmap, path, null,  
  116.                     null);  
  117.             imageListener.onResponse(container, true);  
  118.         }  
  119.     }  
  120.   
  121.     /** 
  122.      * 复写onGetImageSuccess,增加线程池来管理持久化 
  123.      */  
  124.     @Override  
  125.     protected void onGetImageSuccess(String cacheKey, Bitmap response) {  
  126.         // 持久化图片缓存  
  127.         File file = new File(mLocalDirsPath, mLocalName.getFileName(cacheKey));  
  128.         if (!file.exists()) {  
  129.             mPool.execute(new LocalTask(file.getAbsolutePath(), response));  
  130.         }  
  131.         super.onGetImageSuccess(cacheKey, response);  
  132.         // 从请求Map中移除这条请求,因为它已经完成  
  133.         mRequests.remove(cacheKey);  
  134.     }  
  135.   
  136.     @Override  
  137.     protected void onGetImageError(String cacheKey, VolleyError error) {  
  138.         super.onGetImageError(cacheKey, error);  
  139.         // 从请求Map中移除这条请求,因为它已经完成  
  140.         mRequests.remove(cacheKey);  
  141.     }  
  142.   
  143.     /** 
  144.      * <p> 
  145.      * <b>持久化图片到本地的Task</b> 
  146.      * </P> 
  147.      *  
  148.      * @version 1.0 
  149.      * @since 1.0 
  150.      * @author Shun 
  151.      *  
  152.      */  
  153.     private class LocalTask implements Runnable {  
  154.   
  155.         private String mPath;  
  156.         private Bitmap mBitmap;  
  157.   
  158.         public LocalTask(String path, Bitmap bitmap) {  
  159.             this.mPath = path;  
  160.             this.mBitmap = bitmap;  
  161.         }  
  162.   
  163.         @Override  
  164.         public void run() {  
  165.             FileOutputStream out = null;  
  166.             try {  
  167.                 if (null != mBitmap) {  
  168.                     out = new FileOutputStream(mPath);  
  169.                     mBitmap.compress(CompressFormat.JPEG, 100, out);  
  170.                 }  
  171.             } catch (FileNotFoundException e) {  
  172.                 e.printStackTrace();  
  173.             } finally {  
  174.                 if (out != null) {  
  175.                     try {  
  176.                         out.close();  
  177.                         out = null;  
  178.                     } catch (IOException e) {  
  179.                         e.printStackTrace();  
  180.                     }  
  181.                 }  
  182.             }  
  183.         }  
  184.     }  
  185.   
  186.     /** 
  187.      * 加载本地图片Task 
  188.      *  
  189.      * @version 1.0 
  190.      * @since 1.0 
  191.      * @author Shun 
  192.      *  
  193.      */  
  194.     private class LoadLocalTask implements Runnable {  
  195.         String mCacheKey;  
  196.         String mPath;  
  197.         String mUrl;  
  198.         ImageListener mImageListener;  
  199.         int mWidth;  
  200.         int mHeight;  
  201.   
  202.         public LoadLocalTask(String cacheKey, String path, String url,  
  203.                 ImageListener imageListener, int width, int height) {  
  204.             this.mCacheKey = cacheKey;  
  205.             this.mPath = path;  
  206.             this.mUrl = url;  
  207.             this.mImageListener = imageListener;  
  208.             this.mWidth = width;  
  209.             this.mHeight = height;  
  210.         }  
  211.   
  212.         @Override  
  213.         public void run() {  
  214.             Bitmap bitmap = XKImageUtils.loadBitmap(mPath, mWidth, mHeight);  
  215.             if (null != bitmap) {  
  216.                 mCache.putBitmap(mCacheKey, bitmap);  
  217.             }  
  218.             // 这个Hanlder是父类的全局变量  
  219.             mHandler.postDelayed(new DeliverTask(bitmap, mUrl, mImageListener),  
  220.                     100);  
  221.         }  
  222.     }  
  223.   
  224.     /** 
  225.      * 转发到主线程的Task 
  226.      *  
  227.      * @version 1.0 
  228.      * @since 1.0 
  229.      * @author Shun 
  230.      *  
  231.      */  
  232.     private class DeliverTask implements Runnable {  
  233.         Bitmap mCacheBitmap;  
  234.         String mUrl;  
  235.         ImageListener mImageListener;  
  236.   
  237.         public DeliverTask(Bitmap cacheBitmap, String url,  
  238.                 ImageListener imageListener) {  
  239.             this.mCacheBitmap = cacheBitmap;  
  240.             this.mUrl = url;  
  241.             this.mImageListener = imageListener;  
  242.         }  
  243.   
  244.         @Override  
  245.         public void run() {  
  246.             ImageContainer container = new ImageContainer(mCacheBitmap, mUrl,  
  247.                     nullnull);  
  248.             if (null != mCacheBitmap)  
  249.                 mImageListener.onResponse(container, true);  
  250.             else  
  251.                 mImageListener.onErrorResponse(null);  
  252.         }  
  253.     }  
  254.   
  255.     /** 
  256.      *  
  257.      * <p> 
  258.      * <b>取消所有图片请求</b> 
  259.      * </p> 
  260.      * 注意的是,此类接口对本地图片无效 
  261.      */  
  262.     public void cancelRequest() {  
  263.         for (String key : mRequests.keySet()) {  
  264.             ImageContainer container = mRequests.get(key);  
  265.             if (null != container)  
  266.                 container.cancelRequest();  
  267.         }  
  268.         mRequests.clear();  
  269.     }  
  270.   
  271.     /** 
  272.      *  
  273.      * <p> 
  274.      * <b>取消指定的请求</b> 
  275.      * </p> 
  276.      *  
  277.      * @param url 
  278.      *            请求的URL 
  279.      */  
  280.     public void cancelRequest(String url) {  
  281.         cancelRequest(url, 00);  
  282.     }  
  283.   
  284.     /** 
  285.      *  
  286.      * <p> 
  287.      * <b>取消指定的请求</b> 
  288.      * </p> 
  289.      *  
  290.      * @param url 
  291.      *            请求的URL 
  292.      * @param maxWidth 
  293.      *            加载时设置的宽度 
  294.      * @param maxHeight 
  295.      *            加载时设置的高度 
  296.      */  
  297.     public void cancelRequest(String url, int maxWidth, int maxHeight) {  
  298.         String key = getCacheKey(url, maxWidth, maxHeight);  
  299.         ImageContainer container = mRequests.remove(key);  
  300.         if (null != container) {  
  301.             container.cancelRequest();  
  302.         }  
  303.     }  
  304.   
  305.     /** 
  306.      *  
  307.      * <p> 
  308.      * <b>清除所有缓存</b> 
  309.      * </p> 
  310.      */  
  311.     public void clearCache() {  
  312.         if (null != mCache) {  
  313.             mCache.clear();  
  314.         }  
  315.     }  
  316.   
  317.     /** 
  318.      *  
  319.      * <p> 
  320.      * <b>清除指定缓存</b> 
  321.      * </p> 
  322.      *  
  323.      * @param urlOrPath 
  324.      *            可以是Url可以是本地路径 
  325.      */  
  326.     public boolean cearCache(String urlOrPath) {  
  327.         return clearCache(urlOrPath, 00);  
  328.     }  
  329.   
  330.     /** 
  331.      *  
  332.      * <p> 
  333.      * <b>清除指定缓存</b> 
  334.      * </p> 
  335.      * 注意maxWidth,maxHeight必须与缓存请求时的大小保持一致 
  336.      *  
  337.      * @param urlOrPath 
  338.      *            可以是Url可以是本地路径 
  339.      * @param maxWidth 
  340.      *            加载时设置的宽度 
  341.      * @param maxHeight 
  342.      *            加载时设置的高度 
  343.      */  
  344.     public boolean clearCache(String urlOrPath, int maxWidth, int maxHeight) {  
  345.         if (null != mCache) {  
  346.             Bitmap bitmap = mCache.remove(getCacheKey(urlOrPath, maxWidth,  
  347.                     maxHeight));  
  348.             if (null != bitmap) {  
  349.                 if (!bitmap.isRecycled()) {  
  350.                     bitmap.recycle();  
  351.                 }  
  352.                 bitmap = null;  
  353.                 return true;  
  354.             } else {  
  355.                 return false;  
  356.             }  
  357.         }  
  358.         return false;  
  359.     }  
  360. }  
可以看到 我重写了onGetImageSuccess,onGetImageError,get 3个接口来实现本地化功能.大家可以看一下,注释先的很清楚,DeliverTask主要用于异步加载本地图片后调度到主线程.LocalTask主要用于图片的持久化.LoadLocalTask加载本地图片.需要注意的是mHandler. mHandler在源码中是私有的.我自己改成了受保护的.

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

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

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

至此,一个支持本地功能的ImageLoader完成,看注释应该很容易看懂代码,这里就不细讲了 :)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值