一、Volley工作流程图:
NetworkResponse networkResponse = mNetwork.performRequest(request);
看一下mNetwork的定义:(定义在NetworkDispatcher中)
/** The network interface for processing requests. */private final Network mNetwork;
NetworkDispatcher.mNetwork初始化发生在RequestQueue.start()中:
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,mCache, mDelivery);
而RequestQueue.mNetwork是在其构造函数中传入的:
public RequestQueue(Cache cache, Network network, int threadPoolSize,ResponseDelivery delivery) {mCache = cache;mNetwork = network;mDispatchers = new NetworkDispatcher[threadPoolSize];mDelivery = delivery;}
由前面分析知RequestQueue的构建是在Volley.newRequestQueue中实现的:
//创建以stack为參数的Network对象Network network = new BasicNetwork(stack);//创建RequestQueue对象RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);queue.start();//继续向下分析的入口
HttpStack
处理请求,并将结果转换为可被ResponseDelivery
处理的NetworkResponse
。 @Overridepublic NetworkResponse performRequest(Request<?> request) throws VolleyError {long requestStart = SystemClock.elapsedRealtime();while (true) {HttpResponse httpResponse = null;byte[] responseContents = null;Map<String, String> responseHeaders = new HashMap<String, String>();try {/** 忽略网络处理的细节*/// Gather headers.Map<String, String> headers = new HashMap<String, String>();addCacheHeaders(headers, request.getCacheEntry());/**运行网络请求* 这里调用了HttpStack.performRequest。并得到一个HttpResponse返回结果*/httpResponse = mHttpStack.performRequest(request, headers);StatusLine statusLine = httpResponse.getStatusLine();int statusCode = statusLine.getStatusCode();responseHeaders = convertHeaders(httpResponse.getAllHeaders());/**新奇度验证:* 304 Not Modified:client有缓冲的文件并发出了一个条件性的请求* (通常是提供If-Modified-Since头表示客户仅仅想比指定日期更新的文档)。* server告诉客户。原来缓冲的文档还能够继续使用。*/if (statusCode == HttpStatus.SC_NOT_MODIFIED) {/** 解析成NetworkResponse,返回*/return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,request.getCacheEntry().data, responseHeaders, true);}// 推断responses是否有实体信息,一些响应如204,并不包括content。所以须要验证if (httpResponse.getEntity() != null) {//实体信息转化成byte[]responseContents = entityToBytes(httpResponse.getEntity());} else {// 无实体信息情况responseContents = new byte[0];}// 超时情况处理.long requestLifetime = SystemClock.elapsedRealtime() - requestStart;logSlowRequests(requestLifetime, request, responseContents, statusLine);if (statusCode < 200 || statusCode > 299) {throw new IOException();}return new NetworkResponse(statusCode, responseContents, responseHeaders, false);} catch (SocketTimeoutException e) {attemptRetryOnException("socket", request, new TimeoutError());} catch (ConnectTimeoutException e) {attemptRetryOnException("connection", request, new TimeoutError());} catch (MalformedURLException e) {throw new RuntimeException("Bad URL " + request.getUrl(), e);} catch (IOException e) {...}}}
总结一下Network.performRequest所做的工作:
1、由传入的HttpStack对象运行网络请求:mHttpStack.performRequest()
2、解析响应结果,将HttpResponse解析成NetworkResponse;
3、对返回结果进行新奇度验证(304)
4、将response的实体信息转化为byte数组
5、超时情况处理,假设发生超时,认证失败等错误。进行重试操作(attemptRetryOnException)。直到成功、抛出异常(不满足重试策略等)结束。
attemptRetryOnException()是依据重试策略进行请求重试操作:
/*** Attempts to prepare the request for a retry. If there are no more attempts remaining in the* request's retry policy, a timeout exception is thrown.*/private static void attemptRetryOnException(String logPrefix, Request<?
> request, VolleyError exception) throws VolleyError { RetryPolicy retryPolicy = request.getRetryPolicy(); int oldTimeout = request.getTimeoutMs(); try { retryPolicy.retry(exception); } catch (VolleyError e) { request.addMarker( String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout)); throw e; } request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout)); }
前面已经分析过。Android2.3之前使用 HttpClientStack,之后使用HurlStack。
public interface HttpStack {/*** Performs an HTTP request with the given parameters.* <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,* and the Content-Type header is set to request.getPostBodyContentType().</p>* @param request the request to perform* @param 发起请求之前,加入额外的请求 Headers {@link Request#getHeaders()}*/public HttpResponse performRequest(Request<?
> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError; }
2、HttpClientStack(使用HttpClient来实现)
@Overridepublic HttpResponse performRequest(Request<?
> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);//见附一 addHeaders(httpRequest, additionalHeaders); addHeaders(httpRequest, request.getHeaders()); onPrepareRequest(httpRequest);// Nothing.空函数,用于重写;该函数在request被excute之前被调用 //一些网络设置 HttpParams httpParams = httpRequest.getParams(); int timeoutMs = request.getTimeoutMs(); // TODO: Reevaluate this connection timeout based on more wide-scale // data collection and possibly different for wifi vs. 3G. HttpConnectionParams.setConnectionTimeout(httpParams, 5000); HttpConnectionParams.setSoTimeout(httpParams, timeoutMs); return mClient.execute(httpRequest); }
附一:createHttpRequest函数:
/*** 依据传进来的request来构造合适的HttpUriRequest*/static HttpUriRequest createHttpRequest(Request<?> request,Map<String, String> additionalHeaders) throws AuthFailureError {switch (request.getMethod()) {case Method.DEPRECATED_GET_OR_POST: {// This is the deprecated way that needs to be handled for backwards compatibility.// If the request's post body is null, then the assumption is that the request is// GET. Otherwise, it is assumed that the request is a POST.byte[] postBody = request.getPostBody();if (postBody != null) {HttpPost postRequest = new HttpPost(request.getUrl());postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());HttpEntity entity;entity = new ByteArrayEntity(postBody);postRequest.setEntity(entity);return postRequest;} else {return new HttpGet(request.getUrl());}}/***********一般较多使用的是POST与GET。其等同于HttpClient的一般使用流程***************/case Method.GET:return new HttpGet(request.getUrl());case Method.DELETE:return new HttpDelete(request.getUrl());case Method.POST: {HttpPost postRequest = new HttpPost(request.getUrl());//这里就看到了前面实现Request时,重写getBodyContentType()函数的意义postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());setEntityIfNonEmptyBody(postRequest, request);return postRequest;}case Method.PUT: {HttpPut putRequest = new HttpPut(request.getUrl());putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());setEntityIfNonEmptyBody(putRequest, request);return putRequest;}default:throw new IllegalStateException("Unknown request method.");}}
3、HurlStack(由HttpURLConnection来实现) @Overridepublic HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)throws IOException, AuthFailureError {String url = request.getUrl();HashMap<String, String> map = new HashMap<String, String>();map.putAll(request.getHeaders());map.putAll(additionalHeaders);//UrlRewriter见附一if (mUrlRewriter != null) {String rewritten = mUrlRewriter.rewriteUrl(url);if (rewritten == null) {thrownew IOException("URL blocked by rewriter: " + url);}url = rewritten;}/**************HttpURLConnection的一般使用流程*******************/URL parsedUrl = new URL(url);HttpURLConnection connection = openConnection(parsedUrl, request);for (String headerName : map.keySet()) {connection.addRequestProperty(headerName, map.get(headerName));}setConnectionParametersForRequest(connection, request);// Initialize HttpResponse with data from the HttpURLConnection.ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);int responseCode = connection.getResponseCode();if (responseCode == -1) {// -1 is returned by getResponseCode() if the response code could not be retrieved.// Signal to the caller that something was wrong with the connection.thrownew IOException("Could not retrieve response code from HttpUrlConnection.");}StatusLine responseStatus = new BasicStatusLine(protocolVersion,connection.getResponseCode(), connection.getResponseMessage());BasicHttpResponse response = new BasicHttpResponse(responseStatus);response.setEntity(entityFromConnection(connection));for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {if (header.getKey() != null) {Header h = new BasicHeader(header.getKey(), header.getValue().get(0));response.addHeader(h);}}return response;}
附一:UrlRewriter
/** 对URLs在使用前进行重写转换*/public interface UrlRewriter {/*** Returns a URL to use instead of the provided one, or null to indicate* this URL should not be used at all.*/public String rewriteUrl(String originalUrl);}
參数mUrlRewriter通过HttpStack的构造函数传入进来,故能够自行进行定义: public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {mUrlRewriter = urlRewriter;mSslSocketFactory = sslSocketFactory;}
NetworkResponse networkResponse = mNetwork.performRequest(request);
int statusCode
Http 响应状态码byte[] data
Body 数据Map<String, String> headers
响应 Headersboolean notModified
表示是否为 304 响应long networkTimeMs
请求耗时
public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,boolean notModified) {this.statusCode = statusCode;this.data = data;this.headers = headers;this.notModified = notModified;}public NetworkResponse(byte[] data) {this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false);}public NetworkResponse(byte[] data, Map<String, String> headers) {this(HttpStatus.SC_OK, data, headers, false);}
3)回想一下前面分析的设计NetworkResponse的类之间数据的传递关系: public RequestQueue(Cache cache, Network network, int threadPoolSize) {this(cache, network, threadPoolSize,new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
public ExecutorDelivery(final Handler handler) {// Make an Executor that just wraps the handler.mResponsePoster = new Executor() {@Overridepublic void execute(Runnable command) {handler.post(command);}};}
能够看到非常easy,就是使用主线程的Looper构建一个Handler。以下全部的post操作都是调用这个Handler来运行Runnable;比方:
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {request.markDelivered();request.addMarker("post-response");mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
将传递来的Response转化为ResponseDeliveryRunnable ,显然这是一个Runnable;
private class ResponseDeliveryRunnable implements Runnable {private final Request mRequest;private final Response mResponse;private final Runnable mRunnable;public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {mRequest = request;mResponse = response;mRunnable = runnable;}@SuppressWarnings("unchecked")@Overridepublic void run() {// If this request has canceled, finish it and don't deliver.if (mRequest.isCanceled()) {mRequest.finish("canceled-at-delivery");return;}// Deliver a normal response or error, depending.if (mResponse.isSuccess()) {mRequest.deliverResponse(mResponse.result);} else {mRequest.deliverError(mResponse.error);}// If this is an intermediate response, add a marker, otherwise we're done// and the request can be finished.if (mResponse.intermediate) {mRequest.addMarker("intermediate-response");} else {mRequest.finish("done");}// If we have been provided a post-delivery runnable, run it.if (mRunnable != null) {mRunnable.run();}}}
在这个子线程中,转而调用 Request来deliverResponse: 以StringRequest为例,来看这个函数:
@Override
protected void deliverResponse(String response) {if (mListener != null) {mListener.onResponse(response);}
}
这个Listener就是自己在定义Request的时候声明的ResponseListener,能够看到这个Listener工作在子线程中,所以假设要更新界面,注意使用Handler把消息传递主线程进行处理。//创建ImageLoader
imageLoader = new ImageLoader(httpUtils.getRequestQueue(), imageCache);
public ImageLoader(RequestQueue queue, ImageCache imageCache) {mRequestQueue = queue;mCache = imageCache;}
// 获取最大内存缓存大小
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
MAX_CACHE_SIZE = maxMemory / 8; // 定义为应用最大缓存的1/8mImageLruCache = new LruCache<String, Bitmap>(MAX_CACHE_SIZE){@Overrideprotected int sizeOf(String url, Bitmap bitmap){return bitmap.getRowBytes() * bitmap.getHeight() / 1024;}};// 创建ImageCache
imageCache = new ImageLoader.ImageCache() {@Overridepublic void putBitmap(String url, Bitmap bitmap) {mImageLruCache.put(url, bitmap);}@Overridepublic Bitmap getBitmap(String url) {return mImageLruCache.get(url);}
};
使用LruCache来实现ImageCache接口,实现图片的内存缓存:public interface ImageCache {public Bitmap getBitmap(String url);public void putBitmap(String url, Bitmap bitmap);
}
载入图片时的使用方法:imageListener = ImageLoader.getImageListener(myImageView, default_pg, failed_pg);
imageLoader.get(imageUrl, imageListener);
来到ImageLoader#get: public ImageContainer get(String requestUrl, final ImageListener listener) {return get(requestUrl, listener, 0, 0);}public ImageContainer get(String requestUrl, ImageListener imageListener,int maxWidth, int maxHeight) {return get(requestUrl, imageListener, maxWidth, maxHeight, ImageView.ScaleType.CENTER_INSIDE);}public ImageContainer get(String requestUrl, ImageListener imageListener,int maxWidth, int maxHeight, ImageView.ScaleType scaleType) {// 假设操作不是在主线程,则直接抛出异常throwIfNotOnMainThread();// 为图片的URL创建一个特定的cacheKey,注意这个cache还和图片的大小及scaleType相关final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);// 这里会使用自己定义的LruCache去获取一个Bitmap实例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;}// 假设缓存中不存在,则进行获取ImageContainer imageContainer =new ImageContainer(null, requestUrl, cacheKey, imageListener);// 通知Observer这时能够使用默认的图片imageListener.onResponse(imageContainer, true);// 推断是否已经有了一个同样的请求在等待BatchedImageRequest request = mInFlightRequests.get(cacheKey);if (request != null) {// If it is, add this request to the list of listeners.request.addContainer(imageContainer);return imageContainer;}// 创建一个Request,反复之前的流程Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType,cacheKey);mRequestQueue.add(newRequest);mInFlightRequests.put(cacheKey,new BatchedImageRequest(newRequest, imageContainer));return imageContainer;
}
处理逻辑大致和前面的addRequest同样。首先推断缓存中是否已经存在该url相应的bitmap。假设存在直接返回;假设不存在,先推断是否已经有了一个同样的请求在等待。假设是,把这个请求加入到监听者链表中。假设不存在,则创建一个Request<Bitmap>,加入到RequestQueue中,从网络中去获取;从网络中获取的流程和前面分析的同样。 先来看Request<Bitmap>:
protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth, int maxHeight,ScaleType scaleType, final String cacheKey) {return new ImageRequest(requestUrl, new Listener<Bitmap>() {@Overridepublic void onResponse(Bitmap response) {onGetImageSuccess(cacheKey, response);}}, maxWidth, maxHeight, scaleType, Config.RGB_565, new ErrorListener() {@Overridepublic void onErrorResponse(VolleyError error) {onGetImageError(cacheKey, error);}});
}
实际上返回一个ImageRequest类型。来看其请求成功的响应:即把获得的图片存储到缓存中。 protected void onGetImageSuccess(String cacheKey, Bitmap response) {// 把获取到的图片存储到缓存中mCache.putBitmap(cacheKey, response);// 能够看到假设是多个同样请求在等待,则能够同一时候进行更新处理BatchedImageRequest request = mInFlightRequests.remove(cacheKey);if (request != null) {// Update the response bitmap.request.mResponseBitmap = response;// Send the batched responsebatchResponse(cacheKey, request);}
}
最后NetWork运行的结果会封装成NetWorkResponse。通过ResponseDelivery进行转发,这个类最后会调用Request中deliverResponse方法: @Override
protected void deliverResponse(Bitmap response) {mListener.onResponse(response);
}
这个Listener就是最初定义的ImageListener: 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);}}};
}
能够看到这里终于给View空间设置了图片,以上就是Volley实现图片载入的流程。