Volley库源码分析(下)

Volley库源码分析(上):http://blog.csdn.net/mba16c35/article/details/43944703

Volley库源码分析(下):http://blog.csdn.net/mba16c35/article/details/44589137

网络请求部分分析

关于volley的网络请求部分可以看博客:http://www.cnblogs.com/bvin/p/3291611.html



网络请求中有几个转换解析请求获取响应结果的地方:

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就是这样一步步走来的。


 再详细看下上面第二个方法performRequest(),这个方法是NetworkDispatcher中主要步骤的第二步,其中真正网络请求的工作委托给mHttpStack实现,通过HttpStack实现类就是上图的两个HttpClientStack和HurlStack执行。这两个类的区别在于HttpClientStack是直接用HttpClient的execute()方法执行一个Http请求,而HurlStack就是直接用URL.openConnection()进行连接,这种方式在2.3以上是不能用的,所以分开了这两种方式。
/**
     * @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>。

图片下载部分分析

这部分主要集中在将网络请求转化为图片,以及在未有本地图片时显示空图等。
开发者主要和NetworkImageView打交道,NetworkImageView继承ImageView,可以直接在xml布局中使用NetworkImageView,但是该类具有自动下载图片、缓存图片和在无本地图片时显示空图的功能。
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;
    }
	
	......
}

所以关键在于mImageLoader.get()方法。图片下载中最关键的类是ImageLoader,负责帮助从远端URL下载图片和缓存图片。注意,这个类的实例必须从主线程创建,所有的responses也必须分发到主线程。
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;
	}
	
	......
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陆业聪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值