Volley 的工具类解析
Network
ByteArrayPool
原文解释如下:
|
|
上面已经把 ByteArrayPool 的作用说明白了,下面我们看下代码:
|
|
BasicNetwork
网络请求实现类,针对 HttpStack 网络请求做的一层封装,如下:
|
|
HttpStack 是一个接口,里面只有一个 performRequest() 方法,HurlStack 和 HttpClientStack 都继承自这个接口,如下:
|
|
HurlStack 是 基于 HttpURLConnection 的,如下:
|
|
HttpClientStack 是基于 HttpClient 的,如下:
|
|
Request
StringRequest
|
|
JsonRequest
JsonRequest
|
|
JsonObjectRequest
|
|
JsonArrayRequest
|
|
ImageRequest
|
|
ImageLoader
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476/*** Helper that handles loading and caching images from remote URLs.** The simple way to use this class is to call {@link ImageLoader#get(String, ImageListener)}* and to pass in the default image listener provided by* {@link ImageLoader#getImageListener(ImageView, int, int)}. Note that all function calls to* this class must be made from the main thead, and all responses will be delivered to the main* thread as well.*/public class ImageLoader {/** RequestQueue for dispatching ImageRequests onto. */private final RequestQueue mRequestQueue;/** Amount of time to wait after first response arrives before delivering all responses. */private int mBatchResponseDelayMs = 100;/** The cache implementation to be used as an L1 cache before calling into volley. */private final ImageCache mCache;/*** HashMap of Cache keys -> BatchedImageRequest used to track in-flight requests so* that we can coalesce multiple requests to the same URL into a single network request.*/private final HashMap<String, BatchedImageRequest> mInFlightRequests =new HashMap<String, BatchedImageRequest>();/** HashMap of the currently pending responses (waiting to be delivered). */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 {Bitmap getBitmap(String url);void putBitmap(String url, Bitmap bitmap);}/*** Constructs a new ImageLoader.* @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;}/*** The default implementation of ImageListener which handles basic functionality* of showing a default image until the network response is received, at which point* it will switch to either the actual image or the error image.* @param view The imageView that the listener is associated with.* @param defaultImageResId Default image resource ID to use, or 0 if it doesn't exist.* @param errorImageResId Error image resource ID to use, or 0 if it doesn't exist.*/public static ImageListener getImageListener(final ImageView view,final int defaultImageResId, final int errorImageResId) {return new ImageListener() {public void onErrorResponse(VolleyError error) {if (errorImageResId != 0) {view.setImageResource(errorImageResId);}}public void onResponse(ImageContainer response, boolean isImmediate) {if (response.getBitmap() != null) {view.setImageBitmap(response.getBitmap());} else if (defaultImageResId != 0) {view.setImageResource(defaultImageResId);}}};}/*** Interface for the response handlers on image requests.** The call flow is this:* 1. Upon being attached to a request, onResponse(response, true) will* be invoked to reflect any cached data that was already available. If the* data was available, response.getBitmap() will be non-null.** 2. After a network response returns, only one of the following cases will happen:* - onResponse(response, false) will be called if the image was loaded.* or* - onErrorResponse will be called if there was an error loading the image.*/public interface ImageListener extends ErrorListener {/*** Listens for non-error changes to the loading of the image request.** @param response Holds all information pertaining to the request, as well* as the bitmap (if it is loaded).* @param isImmediate True if this was called during ImageLoader.get() variants.* This can be used to differentiate between a cached image loading and a network* image loading in order to, for example, run an animation to fade in network loaded* images.*/void onResponse(ImageContainer response, boolean isImmediate);}/*** Checks if the item is available in the cache.* @param requestUrl The url of the remote image* @param maxWidth The maximum width of the returned image.* @param maxHeight The maximum height of the returned image.* @return True if the item exists in cache, false otherwise.*/public boolean isCached(String requestUrl, int maxWidth, int maxHeight) {return isCached(requestUrl, maxWidth, maxHeight, ScaleType.CENTER_INSIDE);}/*** Checks if the item is available in the cache.** @param requestUrl The url of the remote image* @param maxWidth The maximum width of the returned image.* @param maxHeight The maximum height of the returned image.* @param scaleType The scaleType of the imageView.* @return True if the item exists in cache, false otherwise.*/public boolean isCached(String requestUrl, int maxWidth, int maxHeight, ScaleType scaleType) {throwIfNotOnMainThread();String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);return mCache.getBitmap(cacheKey) != null;}/*** Returns an ImageContainer for the requested URL.** The ImageContainer will contain either the specified default bitmap or the loaded bitmap.* If the default was returned, the {@link ImageLoader} will be invoked when the* request is fulfilled.** @param requestUrl The URL of the image to be loaded.*/public ImageContainer get(String requestUrl, final ImageListener listener) {return get(requestUrl, listener, 0, 0);}/*** Equivalent to calling {@link #get(String, ImageListener, int, int, ScaleType)} with* {@code Scaletype == ScaleType.CENTER_INSIDE}.*/public ImageContainer get(String requestUrl, ImageListener imageListener,int maxWidth, int maxHeight) {return get(requestUrl, imageListener, maxWidth, maxHeight, ScaleType.CENTER_INSIDE);}/*** Issues a bitmap request with the given URL if that image is not available* in the cache, and returns a bitmap container that contains all of the data* relating to the request (as well as the default image if the requested* image is not available).* @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.* @param scaleType The ImageViews ScaleType used to calculate the needed image size.* @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, ScaleType scaleType) {// only fulfill requests that were initiated from the main thread.throwIfNotOnMainThread();final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);// 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, scaleType,cacheKey);mRequestQueue.add(newRequest);mInFlightRequests.put(cacheKey,new BatchedImageRequest(newRequest, imageContainer));return imageContainer;}protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth, int maxHeight,ScaleType scaleType, final String cacheKey) {return new ImageRequest(requestUrl, new Listener<Bitmap>() {public void onResponse(Bitmap response) {onGetImageSuccess(cacheKey, response);}}, maxWidth, maxHeight, scaleType, Config.RGB_565, new ErrorListener() {public void onErrorResponse(VolleyError error) {onGetImageError(cacheKey, error);}});}/*** Sets the amount of time to wait after the first response arrives before delivering all* responses. Batching can be disabled entirely by passing in 0.* @param newBatchedResponseDelayMs The time in milliseconds to wait.*/public void setBatchedResponseDelay(int newBatchedResponseDelayMs) {mBatchResponseDelayMs = newBatchedResponseDelayMs;}/*** 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.*/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);}}/*** Handler for when an image failed to load.* @param cacheKey The cache key that is associated with the image 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);}}/*** Container object for all of the data surrounding an image request.*/public class ImageContainer {/*** The most relevant bitmap for the container. If the image was in cache, the* Holder to use for the final bitmap (the one that pairs to the requested URL).*/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;}/*** Releases interest in the in-flight request (and cancels it if no one else is listening).*/public void cancelRequest() {if (mListener == null) {return;}BatchedImageRequest request = mInFlightRequests.get(mCacheKey);if (request != null) {boolean canceled = request.removeContainerAndCancelIfNecessary(this);if (canceled) {mInFlightRequests.remove(mCacheKey);}} else {// check to see if it is already batched for delivery.request = mBatchedResponses.get(mCacheKey);if (request != null) {request.removeContainerAndCancelIfNecessary(this);if (request.mContainers.size() == 0) {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;}}/*** Wrapper class used to map a Request to the set of active ImageContainer objects that are* interested in its results.*/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;/** List of all of the active ImageContainers that are interested in the request */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);}/*** Detatches the bitmap container from the request and cancels the request if no one is* left listening.* @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;}}/*** Starts the runnable for batched delivery of responses if it is not already started.* @param cacheKey The cacheKey of the response being delivered.* @param request The BatchedImageRequest to be delivered.*/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() {public 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);}}private void throwIfNotOnMainThread() {if (Looper.myLooper() != Looper.getMainLooper()) {throw new IllegalStateException("ImageLoader must be invoked from the main thread.");}}/*** Creates a cache key for use with the L1 cache.* @param url The URL of the request.* @param maxWidth The max-width of the output.* @param maxHeight The max-height of the output.* @param scaleType The scaleType of the imageView.*/private static String getCacheKey(String url, int maxWidth, int maxHeight, ScaleType scaleType) {return new StringBuilder(url.length() + 12).append("#W").append(maxWidth).append("#H").append(maxHeight).append("#S").append(scaleType.ordinal()).append(url).toString();}}NetworkImageView
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193/*** Handles fetching an image from a URL as well as the life-cycle of the* associated request.*/public class NetworkImageView extends ImageView {/** The URL of the network image to load */private String mUrl;/*** Resource ID of the image to be used as a placeholder until the network image is loaded.*/private int mDefaultImageId;/*** Resource ID of the image to be used if the network response fails.*/private int mErrorImageId;/** Local copy of the ImageLoader. */private ImageLoader mImageLoader;/** Current ImageContainer. (either in-flight or finished) */private ImageContainer mImageContainer;public NetworkImageView(Context context) {this(context, null);}public NetworkImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public NetworkImageView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}/*** Sets URL of the image that should be loaded into this view. Note that calling this will* immediately either set the cached image (if available) or the default image specified by* {@link NetworkImageView#setDefaultImageResId(int)} on the view.** NOTE: If applicable, {@link NetworkImageView#setDefaultImageResId(int)} and* {@link NetworkImageView#setErrorImageResId(int)} should be called prior to calling* this function.** @param url The URL that should be loaded into this ImageView.* @param imageLoader ImageLoader that will be used to make the request.*/public void setImageUrl(String url, ImageLoader imageLoader) {mUrl = url;mImageLoader = imageLoader;// The URL has potentially changed. See if we need to load it.loadImageIfNecessary(false);}/*** Sets the default image resource ID to be used for this view until the attempt to load it* completes.*/public void setDefaultImageResId(int defaultImage) {mDefaultImageId = defaultImage;}/*** Sets the error image resource ID to be used for this view in the event that the image* requested fails to load.*/public void setErrorImageResId(int errorImage) {mErrorImageId = errorImage;}/*** Loads the image for the view if it isn't already loaded.* @param isInLayoutPass True if this was invoked from a layout pass, false otherwise.*/void loadImageIfNecessary(final boolean isInLayoutPass) {int width = getWidth();int height = getHeight();ScaleType scaleType = getScaleType();boolean wrapWidth = false, wrapHeight = false;if (getLayoutParams() != null) {wrapWidth = getLayoutParams().width == LayoutParams.WRAP_CONTENT;wrapHeight = getLayoutParams().height == LayoutParams.WRAP_CONTENT;}// if the view's bounds aren't known yet, and this is not a wrap-content/wrap-content// view, hold off on loading the image.boolean isFullyWrapContent = wrapWidth && wrapHeight;if (width == 0 && height == 0 && !isFullyWrapContent) {return;}// if the URL to be loaded in this view is empty, cancel any old requests and clear the// currently loaded image.if (TextUtils.isEmpty(mUrl)) {if (mImageContainer != null) {mImageContainer.cancelRequest();mImageContainer = null;}setDefaultImageOrNull();return;}// if there was an old request in this view, check if it needs to be canceled.if (mImageContainer != null && mImageContainer.getRequestUrl() != null) {if (mImageContainer.getRequestUrl().equals(mUrl)) {// if the request is from the same URL, return.return;} else {// if there is a pre-existing request, cancel it if it's fetching a different URL.mImageContainer.cancelRequest();setDefaultImageOrNull();}}// Calculate the max image width / height to use while ignoring WRAP_CONTENT dimens.int maxWidth = wrapWidth ? 0 : width;int maxHeight = wrapHeight ? 0 : height;// The pre-existing content of this view didn't match the current URL. Load the new image// from the network.// update the ImageContainer to be the new bitmap container.mImageContainer = mImageLoader.get(mUrl,new ImageListener() {public void onErrorResponse(VolleyError error) {if (mErrorImageId != 0) {setImageResource(mErrorImageId);}}public void onResponse(final ImageContainer response, boolean isImmediate) {// If this was an immediate response that was delivered inside of a layout// pass do not set the image immediately as it will trigger a requestLayout// inside of a layout. Instead, defer setting the image by posting back to// the main thread.if (isImmediate && isInLayoutPass) {post(new Runnable() {public void run() {onResponse(response, false);}});return;}if (response.getBitmap() != null) {setImageBitmap(response.getBitmap());} else if (mDefaultImageId != 0) {setImageResource(mDefaultImageId);}}}, maxWidth, maxHeight, scaleType);}private void setDefaultImageOrNull() {if(mDefaultImageId != 0) {setImageResource(mDefaultImageId);}else {setImageBitmap(null);}}protected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);loadImageIfNecessary(true);}protected void onDetachedFromWindow() {if (mImageContainer != null) {// If the view was bound to an image request, cancel it and clear// out the image from the view.mImageContainer.cancelRequest();setImageBitmap(null);// also clear out the container so we can reload the image if necessary.mImageContainer = null;}super.onDetachedFromWindow();}protected void drawableStateChanged() {super.drawableStateChanged();invalidate();}}
ClearCacheRequest
|
|
Cache
DiskBasedCache
默认缓存路径为硬盘缓存,如下:
|
|
####