Android网络通信Volley框架源码浅析(三)

尊重原创

通过前面浅析(一)和浅析(二)的分析,相信大家对于Volley有了初步的认识,但是如果想更深入的理解,还需要靠大家多多看源码。

这篇文章中我们主要来研究一下使用Volley框架请求大量图片的原理,在Android的应用中,通过http请求获取的数据主要有三类:

1、json2、xml3、Image

其中json和xml的获取其实原理很简单,使用Volley获取感觉有点大财小用了,了解Volley获取图片的原理才是比较有意义的,因为里面涉及到很多知识点,比如获取大量图片如何防止OOM。

那么我们就开始研究源码吧。

(1) ImageLoader.java通过它的名字我们就知道是用来加载Image的工具类

/**通过调用ImageLoader的get方法就可以获取到图片,然后通过一个Listener回调,将图片设置到ImgeView中(这个方法务必在主线程中调用) */public class ImageLoader {/** 前面已经接触过,请求队列(其实不是真实的队列,里面包含了本地队列和网络队列) */private final RequestQueue mRequestQueue;/** 图片缓冲,这个缓存不是前面提到的磁盘缓存,这个是内存缓存,我们可以通过LruCache实现这个接口 */private final ImageCache mCache;/*** 用于存放具有相同cacheKey的请求*/private final HashMap<String, BatchedImageRequest> mInFlightRequests =new HashMap<String, BatchedImageRequest>();/** 用于存放具有相同Key,并且返回了数据的请求*/private final HashMap<String, BatchedImageRequest> mBatchedResponses =new HashMap<String, BatchedImageRequest>();/** Handler to the main thread. */private final Handler mHandler = new Handler(Looper.getMainLooper());/** Runnable for in-flight response delivery. */private Runnable mRunnable;/*** Simple cache adapter interface. If provided to the ImageLoader, it* will be used as an L1 cache before dispatch to Volley. Implementations* must not block. Implementation with an LruCache is recommended.*/public interface ImageCache {public Bitmap getBitmap(String url);public void putBitmap(String url, Bitmap bitmap);}/*** 构造函数需要传入一个RequestQueue对象和一个内存缓存对象* @param queue The RequestQueue to use for making image requests.* @param imageCache The cache to use as an L1 cache.*/public ImageLoader(RequestQueue queue, ImageCache imageCache) {mRequestQueue = queue;mCache = imageCache;}/*** 用于图片获取成功或者失败的回调* @param imageView 需要设置图片的ImageView.* @param defaultImageResId 默认显示图片.* @param errorImageResId 出错时显示的图片.*/public static ImageListener getImageListener(final ImageView view,final int defaultImageResId, final int errorImageResId) {return new ImageListener() {@Overridepublic void onErrorResponse(VolleyError error) {//出错并且设置了出错图片,那么显示出错图片if (errorImageResId != 0) {view.setImageResource(errorImageResId);}}@Overridepublic void onResponse(ImageContainer response, boolean isImmediate) {if (response.getBitmap() != null) {//成功获取到了数据,则显示view.setImageBitmap(response.getBitmap());} else if (defaultImageResId != 0) {//数据为空,那么显示默认图片view.setImageResource(defaultImageResId);}}};}/*** 判断图片是否已经缓存,不同尺寸的图片的cacheKey是不一样的* @param requestUrl 图片的url* @param maxWidth 请求图片的宽度.* @param maxHeight 请求图片的高度.* @return 返回true则缓存.*/public boolean isCached(String requestUrl, int maxWidth, int maxHeight) {throwIfNotOnMainThread();String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);return mCache.getBitmap(cacheKey) != null;}/*** 这个方法时个核心方法,我们主要通过它来获取图片** @param requestUrl The URL of the image to be loaded.* @param defaultImage Optional default image to return until the actual image is loaded.*/public ImageContainer get(String requestUrl, final ImageListener listener) {return get(requestUrl, listener, 0, 0);}/*** 这个方法比上面方法多了两个参数,如果传入则图片大小会做相应处理,如果不传默认为0,图片大小不做处理* @param requestUrl The url of the remote image* @param imageListener The listener to call when the remote image is loaded* @param maxWidth The maximum width of the returned image.* @param maxHeight The maximum height of the returned image.* @return A container object that contains all of the properties of the request, as well as*the currently available image (default if remote is not loaded).*/public ImageContainer get(String requestUrl, ImageListener imageListener,int maxWidth, int maxHeight) {// only fulfill requests that were initiated from the main thread.throwIfNotOnMainThread();//获取key,其实就是url,width,height按照某种格式拼接final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);// 首先从缓存里面取图片Bitmap cachedBitmap = mCache.getBitmap(cacheKey);if (cachedBitmap != null) {// 如果缓存命中,则直接放回ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);imageListener.onResponse(container, true);return container;}// 没有命中,则创建一个ImageContainer,注意此时图片数据传入的null,ImageContainer imageContainer =new ImageContainer(null, requestUrl, cacheKey, imageListener);// 这就是为什么在onResponse中我们需要判断图片数据是否为空,此时就是为空的imageListener.onResponse(imageContainer, true);// 判断同一个key的请求是否已经存在BatchedImageRequest request = mInFlightRequests.get(cacheKey);if (request != null) {// 如果存在,则直接加入request中,没有必要对一个key发送多个请求request.addContainer(imageContainer);return imageContainer;}// 发送一个请求,并加入RequestQueueRequest<?> newRequest =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);}});mRequestQueue.add(newRequest);VolleyLog.e("————–>"+newRequest.getSequence());//加入到HashMap中,表明这个key已经存在一个请求mInFlightRequests.put(cacheKey,new BatchedImageRequest(newRequest, imageContainer));return imageContainer;}/*** Handler for when an image was successfully loaded.* @param cacheKey The cache key that is associated with the image request.* @param response The bitmap that was returned from the network.*/private void onGetImageSuccess(String cacheKey, Bitmap response) {// 获取图片成功,放入缓存mCache.putBitmap(cacheKey, response);// 将cacheKey对应的请求从mInFlightRequests中移除BatchedImageRequest request = mInFlightRequests.remove(cacheKey);if (request != null) {// Update the response bitmap.request.mResponseBitmap = response;// Send the batched responsebatchResponse(cacheKey, request);}}/*** Handler for when an image failed to load.* @param cacheKey The cache key that is associated with the image request.*/private 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);}}/*** Container object for all of the data surrounding an image request.*/public class ImageContainer {/*** 保存从网络获取的图片*/private Bitmap mBitmap;private final ImageListener mListener;/** The cache key that was associated with the request */private final String mCacheKey;/** The request URL that was specified */private final String mRequestUrl;/*** Constructs a BitmapContainer object.* @param bitmap The final bitmap (if it exists).* @param requestUrl The requested URL for this container.* @param cacheKey The cache key that identifies the requested URL for this container.*/public ImageContainer(Bitmap bitmap, String requestUrl,String cacheKey, ImageListener listener) {mBitmap = bitmap;mRequestUrl = requestUrl;mCacheKey = cacheKey;mListener = listener;}/*** 取消一个图片请求*/public void cancelRequest() {if (mListener == null) {return;}//判断此key对应的请求有没有BatchedImageRequest request = mInFlightRequests.get(mCacheKey);if (request != null) {/**如果存在,request中mContainers中的这个Container,如果mContainers的size为0,那么removeContainerAndCancelIfNecessary返回true*/boolean canceled = request.removeContainerAndCancelIfNecessary(this);if (canceled) {//如果返回true,那么说明没有任何一个ImageView对这个请求感兴趣,,需要移除它mInFlightRequests.remove(mCacheKey);}} else {// 判断是否这个request已经成功返回了request = mBatchedResponses.get(mCacheKey);if (request != null) {request.removeContainerAndCancelIfNecessary(this);if (request.mContainers.size() == 0) {//如果已经成功返回,并且没有ImageView对他感兴趣,那么删除它mBatchedResponses.remove(mCacheKey);}}}}/*** Returns the bitmap associated with the request URL if it has been loaded, null otherwise.*/public Bitmap getBitmap() {return mBitmap;}/*** Returns the requested URL for this container.*/public String getRequestUrl() {return mRequestUrl;}}/*** 对Request的一个包装,将所有有共同key的请求放入一个LinkedList中*/private class BatchedImageRequest {/** The request being tracked */private final Request<?> mRequest;/** The result of the request being tracked by this item */private Bitmap mResponseBitmap;/** Error if one occurred for this response */private VolleyError mError;/** 存放具有共同key的ImageContainer*/private final LinkedList<ImageContainer> mContainers = new LinkedList<ImageContainer>();/*** Constructs a new BatchedImageRequest object* @param request The request being tracked* @param container The ImageContainer of the person who initiated the request.*/public BatchedImageRequest(Request<?> request, ImageContainer container) {mRequest = request;mContainers.add(container);}/*** Set the error for this response*/public void setError(VolleyError error) {mError = error;}/*** Get the error for this response*/public VolleyError getError() {return mError;}/*** Adds another ImageContainer to the list of those interested in the results of* the request.*/public void addContainer(ImageContainer container) {mContainers.add(container);}/*** 移除一个ImageContainer,如果此时size==0,那么需要从mInFlightRequests中移除该BatchedImageRequest* @param container The container to remove from the list* @return True if the request was canceled, false otherwise.*/public boolean removeContainerAndCancelIfNecessary(ImageContainer container) {mContainers.remove(container);if (mContainers.size() == 0) {mRequest.cancel();return true;}return false;}}/*** 当请求返回后,将BatchedImageRequest放入到mBatchedResponses,然后将结果发送给所有具有相同key的ImageContainer,ImageContainer通过里面的Listener发送到ImageView,从而显示出来* @param cacheKey The cacheKey of the response being delivered.* @param request The BatchedImageRequest to be delivered.* @param error The volley error associated with the request (if applicable).*/private void batchResponse(String cacheKey, BatchedImageRequest request) {mBatchedResponses.put(cacheKey, request);// If we don’t already have a batch delivery runnable in flight, make a new one.// Note that this will be used to deliver responses to all callers in mBatchedResponses.if (mRunnable == null) {mRunnable = new Runnable() {@Overridepublic void run() {for (BatchedImageRequest bir : mBatchedResponses.values()) {for (ImageContainer container : bir.mContainers) {// If one of the callers in the batched request canceled the request// after the response was received but before it was delivered,// skip them.if (container.mListener == null) {continue;}if (bir.getError() == null) {container.mBitmap = bir.mResponseBitmap;container.mListener.onResponse(container, false);} else {container.mListener.onErrorResponse(bir.getError());}}}mBatchedResponses.clear();mRunnable = null;}};// Post the runnable.mHandler.postDelayed(mRunnable, mBatchResponseDelayMs);}}/*** 获取一个请求的key,拼接规则就是使用#讲几个连接起来* @param url The URL of the request.* @param maxWidth The max-width of the output.* @param maxHeight The max-height of the output.*/private static String getCacheKey(String url, int maxWidth, int maxHeight) {return new StringBuilder(url.length() + 12).append("#W").append(maxWidth).append("#H").append(maxHeight).append(url).toString();}}梦想让我与众不同,奋斗让我改变命运!

Android网络通信Volley框架源码浅析(三)

相关文章:

你感兴趣的文章:

标签云: