网络请求部分分析
网络请求中有几个转换解析请求获取响应结果的地方:
1.HttpStack接口的performRequest()方法
- public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
2.Network接口的performRequest()方法
- public NetworkResponse performRequest(Request<?> request)
3.Request类的parseNetworkResponse()抽象方法
- abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
可以很鲜明得看出第一个是对原生Http请求的解析,解析出来是一个Apach的HttpResponse 实例,这个结果就是通过上述两个HttpStack的实现类HttpClientStack和HurlStack执行获取的获取的。
而第二个解析出来是框架自定的NetworkResponse,这是通过Network的实现类BasicNetwork来获取的。
第三个就是第二个得出来NetworkResponse解析成用户期望Response<T> 了,这个Response和Request是对应的,有String型,json型还有Image型的。然后在通过ResponseDelivery把解析好的结果发送到主线程。
从Request到Response就是这样一步步走来的。
- /**
- * @title performRequest执行各种Request请求并以NetworkResponse的形式返回结果
- * @param Request
- * @return NetworkResponse
- * @throws VolleyError
- * 定义:{@link Network#performRequest(Request)}
- * 被调:{@link NetworkDispatcher#run()}
- *
- */
- @Override//NetworkDispatcher的run()方法中调用
- public NetworkResponse performRequest(Request<?> request) throws VolleyError {
- long requestStart = SystemClock.elapsedRealtime();//开始请求时间
- while (true) {
- HttpResponse httpResponse = null;//apache的请求结果
- 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());//先获取缓存数据
- httpResponse = mHttpStack.performRequest(request, headers);//去调用mHttpStack的实现方法执行请求
- StatusLine statusLine = httpResponse.getStatusLine();//获取http状态线
- int statusCode = statusLine.getStatusCode();//获取状态码
- responseHeaders = convertHeaders(httpResponse.getAllHeaders());
- // Handle cache validation.//处理缓存验证
- if (statusCode == HttpStatus.SC_NOT_MODIFIED) {//返回缓存数据
- return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
- request.getCacheEntry().data, responseHeaders, true);
- }
- //把HttpEntity转化为byte[]数据
- responseContents = entityToBytes(httpResponse.getEntity());
- // if the request is slow, log it.//如果请求很慢,就打印出来看一下
- long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
- logSlowRequests(requestLifetime, request, responseContents, statusLine);//打印
- //连接正常但是返回无内容,抛出IO异常
- if (statusCode != HttpStatus.SC_OK && statusCode != HttpStatus.SC_NO_CONTENT) {
- 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) {//Bad URL
- throw new RuntimeException("Bad URL " + request.getUrl(), e);
- } catch (IOException e) {//IO异常
- int statusCode = 0;
- NetworkResponse networkResponse = null;
- if (httpResponse != null) {
- statusCode = httpResponse.getStatusLine().getStatusCode();
- } else {//如果没有返回httpResponse,就说明没连接
- throw new NoConnectionError(e);
- }
- VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
- if (responseContents != null) {//返回数据不为空
- networkResponse = new NetworkResponse(statusCode, responseContents,
- responseHeaders, false);//创建响应体
- if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
- statusCode == HttpStatus.SC_FORBIDDEN) {//认证失败异常,重试
- attemptRetryOnException("auth",
- request, new AuthFailureError(networkResponse));
- } else {//服务器异常
- // TODO: Only throw ServerError for 5xx status codes.
- throw new ServerError(networkResponse);//只有状态码为5XX才抛出服务器异常
- }
- } else {//网络异常
- throw new NetworkError(networkResponse);
- }
- }
- }
- }
A:先是通过mHttpStack把请求执行并且获取它的响应结果,根据HttpStatus做出各种判断。
B:然后再把httpResponse的Entity转化为ByteArray,并处理各种发生的异常。
C:最后的过程是这样的:通过Volley创建一个RequestQueue请求队列,当这个队列开始运作的时候会启动NetworkDispatcher这个工作线程,而BasicNetwork的performRequest()的方法就在NetworkDispatcher线程run()方法中调用,然后通过mHttpStack的performRequest()方法获取一个networkResponse,在NetworkDispatcher线程把这个networkResponse转化为期望的数据类型,比如Response<String>,Response<Json>,Response<Bitmap>。
图片下载部分分析
- 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;
- ......
- /**
- * 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.
- */
- private void loadImageIfNecessary(final boolean isInLayoutPass) {
- int width = getWidth();
- int height = getHeight();
- ......
- // The pre-existing content of this view didn't match the current URL. Load the new image
- // from the network.
- ImageContainer newContainer = mImageLoader.get(mUrl,
- new ImageListener() {
- @Override
- public void onErrorResponse(VolleyError error) {
- if (mErrorImageId != 0) {
- setImageResource(mErrorImageId);
- }
- }
- @Override
- 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() {
- @Override
- public void run() {
- onResponse(response, false);
- }
- });
- return;
- }
- if (response.getBitmap() != null) {
- setImageBitmap(response.getBitmap());
- } else if (mDefaultImageId != 0) {
- setImageResource(mDefaultImageId);
- }
- }
- });
- // update the ImageContainer to be the new bitmap container.
- mImageContainer = newContainer;
- }
- ......
- }
- 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). */
- //BatchedImageRequest用于匹配一个Request和一组对该Request的返回结果感兴趣的ImageContainer,这样可以把相同的Request合并
- 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;
- ......
- /**
- * 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.
- * @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();//必须在主线程创建requests
- 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);//ImageContainer是一个数据类
- // 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<?> newRequest =
- new ImageRequest(requestUrl, new Listener<Bitmap>() {
- @Override
- public void onResponse(Bitmap response) {
- onGetImageSuccess(cacheKey, response);
- }
- }, maxWidth, maxHeight,
- Config.RGB_565, new ErrorListener() {
- @Override
- public void onErrorResponse(VolleyError error) {
- onGetImageError(cacheKey, error);
- }
- });//ImageRequest具有将原始网络请求编码成Bitmap的功能
- mRequestQueue.add(newRequest);//向mRequestQueue增加ImageRequest,注意各个线程都只和同步队列打交道,各个线程因此解耦
- mInFlightRequests.put(cacheKey,
- new BatchedImageRequest(newRequest, imageContainer));
- return imageContainer;
- }
- ......
- }