Volley简单学习使用五—— 源码分析三

来源:互联网 发布:世界各国经济数据 编辑:程序博客网 时间:2024/05/16 03:50

一、Volley工作流程图:


二、Network
    在NetworkDispatcher中需要处理的网络请求,由下面进行处理:
    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();//继续向下分析的入口

    可以看出mNetwork其实是BasicNetwork对象。
    则NetworkResponse中mNetwork实际上调用的是BasicNetwork.performRequest(),这是一个专门用来处理网络请求的函数,其作用为调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse
看一下其源码:
   @Override    public 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:客户端有缓冲的文件并发出了一个条件性的请求                 * (一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。                 * 服务器告诉客户,原来缓冲的文档还可以继续使用。*/                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));    }
三、HttpClientStack、HurlStack
    据上面源码知,网络请求处理的逻辑实际上是交由传进来的参数HttpStack进行处理。前面已经分析过,Android2.3之前使用 HttpClientStack,之后使用HurlStack;
1、先看两者的父类HttpStack:
    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来实现)

    @Override    public 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来实现)
    @Override    public 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
    回到起点NetworkDispatcher(Thread)中的run()函数,其中:

    NetworkResponse networkResponse = mNetwork.performRequest(request);

下面继续看NetworkResponse的源码:
NetworkResponse类很简单,仅是用以在多个类中传递数据,其成员变量:
1)成员变量
int statusCode Http 响应状态码

byte[] data Body 数据
Map<String, String> headers 响应 Headers
boolean notModified 表示是否为 304 响应
long networkTimeMs 请求耗时

2)其主体只为几个构造函数:
    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的类之间数据的传递关系:



这里的主体是根据NetworkDispatcher.run()函数进行分析的
0、函数中调用Network.performRequest();
     NetworkResponse networkResponse = mNetwork.performRequest(request);
     而Network.performRequest()是基于HttpStack实现的;
1、HttpClientStack与HurlStack(分别基于HttpClient与HttpURLConnection实现)中的public HttpResponse performRequest()函数返回HttpResponse ;
2、Network(实际为BasicNetwork)中performRequest()方法,使用1中的两个HttpStack类,获取到其返回值HttpResponse,然后将其解析成为NetworkResponse;
3、Request中 abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
    将NetworkResponse解析成Response;
    而该函数的调用是在NetworkDispatcher中的run()函数中调用的;                
4、在NetworkDispatcher.run()的最后一步:
    mDelivery.postResponse(request, response);
    将response传递给了ResponseDelivery
后面继续看Delivery的逻辑;
ResponseDelivery mDelivery的实际类型是ExecutorDelivery:
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() {        @Override        public void execute(Runnable command) {            handler.post(command);        }    };}
可以看到很简单,就是使用主线程的Looper构建一个Handler,下面所有的post操作都是调用这个Handler来执行Runnable;

比如:

@Overridepublic 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")    @Override    public 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为例,来看这个函数:

@Overrideprotected void deliverResponse(String response) {    if (mListener != null) {        mListener.onResponse(response);    }}
这个Listener就是自己在定义Request的时候声明的ResponseListener,可以看到这个Listener工作在子线程中,所以如果要更新界面,注意使用Handler把消息传递主线程进行处理。

***************************************************** Volley图片加载的实现 *******************************************************
Volley的图片加载主要还是基于上面的原理来实现的,具体如下:
ImageLoader的使用:
//创建ImageLoaderimageLoader = new ImageLoader(httpUtils.getRequestQueue(), imageCache);public ImageLoader(RequestQueue queue, ImageCache imageCache) {    mRequestQueue = queue;    mCache = imageCache;}
这里面的ImageCache是自定义的:
// 获取最大内存缓存大小int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);MAX_CACHE_SIZE = maxMemory / 8; // 定义为应用最大缓存的1/8mImageLruCache = new LruCache<String, Bitmap>(MAX_CACHE_SIZE){    @Override    protected int sizeOf(String url, Bitmap bitmap){        return bitmap.getRowBytes() * bitmap.getHeight() / 1024;    }};// 创建ImageCacheimageCache = new ImageLoader.ImageCache() {    @Override    public void putBitmap(String url, Bitmap bitmap) {        mImageLruCache.put(url, bitmap);    }    @Override    public 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>() {        @Override        public void onResponse(Bitmap response) {            onGetImageSuccess(cacheKey, response);        }    }, maxWidth, maxHeight, scaleType, Config.RGB_565, new ErrorListener() {        @Override        public 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 response        batchResponse(cacheKey, request);    }}
最后NetWork执行的结果会封装成NetWorkResponse,通过ResponseDelivery进行转发,这个类最后会调用Request中deliverResponse方法:

@Overrideprotected 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() {        @Override        public void onErrorResponse(VolleyError error) {            if (errorImageResId != 0) {                view.setImageResource(errorImageResId);            }        }        @Override        public void onResponse(ImageContainer response, boolean isImmediate) {            if (response.getBitmap() != null) {                view.setImageBitmap(response.getBitmap());            } else if (defaultImageResId != 0) {                view.setImageResource(defaultImageResId);            }        }    };}

可以看到这里最终给View空间设置了图片,以上就是Volley实现图片加载的流程。

0 0
原创粉丝点击