volley(3)——源码分析

            // Mark the response as intermediate.

            response.intermediate = true;

            // Post the intermediate response back to the user and have

            // the delivery then forward the request along to the network.

            mDelivery.postResponse(request, response, new Runnable() {

                @Override

                public void run() {

                    try {

                        mNetworkQueue.put(request);

                    } catch (InterruptedException e) {

                        // Not much we can do about this.

                    }

                }

            });

        }

    } catch (InterruptedException e) {

        // We may have been interrupted because it was time to quit.

        if (mQuit) {

            return;

        }

        continue;

    }

}

}




首先通过Process.setThreadPriority(Process.THREAD\_PRIORITY\_BACKGROUND);设置线程优先级,然后通过mCache.initialize(); 初始化缓存块,其中mCache是由Volley.java中的`newRequestQueue(Context context, HttpStack stack)`方法中实例化传入的,其Cache接口的实现为new DiskBasedCache(cacheDir),其中cacheDir默认在Volley.java中不设置为/data/data/app-package/cache/volley/。



接下来由while (true)可以发现缓存线程是一直在执行,其中通过mQuit标记进制是否结束线程的操作。mCacheQueue.take()从阻塞队列获取最前面的一个request,没有request就阻塞等待。接着通过mCache.get(request.getCacheKey());尝试从缓存中取出响应结果,如何为空的话则把这条请求加入到网络请求队列中,如果不为空的话再判断该缓存是否已过期,如果已经过期了则同样把这条请求加入到网络请求队列中,否则就认为不需要重发网络请求,直接使用缓存中的数据即可。在这个过程中调运了parseNetworkResponse()方法来对数据进行解析,再往后就是将解析出来的数据进行回调了。现在先来看下Request抽象基类的这部分代码:



/**

  • Subclasses must implement this to parse the raw network response

  • and return an appropriate response type. This method will be

  • called from a worker thread. The response will not be delivered

  • if you return null.

  • @param response Response from the network

  • @return The parsed response, or null in the case of an error

*/

abstract protected Response parseNetworkResponse(NetworkResponse response);




通过注释可以看到他就是一个解析模块的功能。



上面说了,当调用了Volley.newRequestQueue(context)之后,就会有五个线程一直在后台运行,不断等待网络请求的到来,其中一个CacheDispatcher是缓存线程,四个NetworkDispatcher是网络请求线程。CacheDispatcher的run方法刚才已经大致分析了,解析来看下NetworkDispatcher中是怎么处理网络请求队列的,具体代码如下所示:



@Override

public void run() {

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

while (true) {

    long startTimeMs = SystemClock.elapsedRealtime();

    Request<?> request;

    try {

        // Take a request from the queue.

        request = mQueue.take();

    } catch (InterruptedException e) {

        // We may have been interrupted because it was time to quit.

        if (mQuit) {

            return;

        }

        continue;

    }

    try {

        request.addMarker("network-queue-take");



        // If the request was cancelled already, do not perform the

        // network request.

        if (request.isCanceled()) {

            request.finish("network-discard-cancelled");

            continue;

        }

        addTrafficStatsTag(request);

        // Perform the network request.

        NetworkResponse networkResponse = mNetwork.performRequest(request);

        request.addMarker("network-http-complete");

        // If the server returned 304 AND we delivered a response already,

        // we're done -- don't deliver a second identical response.

        if (networkResponse.notModified && request.hasHadResponseDelivered()) {

            request.finish("not-modified");

            continue;

        }

        // Parse the response here on the worker thread.

        Response<?> response = request.parseNetworkResponse(networkResponse);

        request.addMarker("network-parse-complete");

        // Write to cache if applicable.

        // TODO: Only update cache metadata instead of entire record for 304s.

        if (request.shouldCache() && response.cacheEntry != null) {

            mCache.put(request.getCacheKey(), response.cacheEntry);

            request.addMarker("network-cache-written");

        }

        // Post the response back.

        request.markDelivered();

        mDelivery.postResponse(request, response);

    } catch (VolleyError volleyError) {

        volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);

        parseAndDeliverNetworkError(request, volleyError);

    } catch (Exception e) {

        VolleyLog.e(e, "Unhandled exception %s", e.toString());

        VolleyError volleyError = new VolleyError(e);

        volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);

        mDelivery.postError(request, volleyError);

    }

}

}




和CacheDispatcher差不多,如上可以看见一个类似的while(true)循环,说明网络请求线程也是在不断运行的。



如上通过mNetwork.performRequest(request);代码来发送网络请求,而Network是一个接口,这里具体的实现之前已经分析是BasicNetwork,所以先看下它的performRequest()方法,如下所示:



NetWork接口的代码:



public interface Network {

/**

 * Performs the specified request.

 * @param request Request to process

 * @return A {@link NetworkResponse} with data and caching metadata; will never be null

 * @throws VolleyError on errors

 */

public NetworkResponse performRequest(Request<?> request) throws VolleyError;

}




上面说了,就是执行指定的请求。他的BasicNetwork实现子类如下:



@Override

public NetworkResponse performRequest(Request<?> request) throws VolleyError {

long requestStart = SystemClock.elapsedRealtime();

while (true) {

    HttpResponse httpResponse = null;

    byte[] responseContents = null;

    Map<String, String> responseHeaders = Collections.emptyMap();

    try {

        // Gather headers.

        Map<String, String> headers = new HashMap<String, String>();

        addCacheHeaders(headers, request.getCacheEntry());

        httpResponse = mHttpStack.performRequest(request, headers);

        StatusLine statusLine = httpResponse.getStatusLine();

        int statusCode = statusLine.getStatusCode();



        responseHeaders = convertHeaders(httpResponse.getAllHeaders());

        // Handle cache validation.

        if (statusCode == HttpStatus.SC_NOT_MODIFIED) {

            Entry entry = request.getCacheEntry();

            if (entry == null) {

                return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,

                        responseHeaders, true,

                        SystemClock.elapsedRealtime() - requestStart);

            }

            // A HTTP 304 response does not have all header fields. We

            // have to use the header fields from the cache entry plus

            // the new ones from the response.

            // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5

            entry.responseHeaders.putAll(responseHeaders);

            return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,

                    entry.responseHeaders, true,

                    SystemClock.elapsedRealtime() - requestStart);

        }

        // Some responses such as 204s do not have content.  We must check.

        if (httpResponse.getEntity() != null) {

            responseContents = entityToBytes(httpResponse.getEntity());

        } else {

            // Add 0 byte response as a way of honestly representing a

            // no-content request.

            responseContents = new byte[0];

        }

        // if the request is slow, log it.

        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,

                SystemClock.elapsedRealtime() - requestStart);

    } 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) {

        int statusCode = 0;

        NetworkResponse networkResponse = null;

        if (httpResponse != null) {

            statusCode = httpResponse.getStatusLine().getStatusCode();

        } else {

            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, SystemClock.elapsedRealtime() - requestStart);

            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);

            }

        } else {

            throw new NetworkError(networkResponse);

        }

    }

}

}




这个方法是网络请求的具体实现,也是一个大while循环,其中mHttpStack.performRequest(request, headers);代码中的mHttpStack是Volley的newRequestQueue()方法中创建的实例,前面已经说过,这两个对象的内部实际就是分别使用HttpURLConnection和HttpClient来发送网络请求的,然后把服务器返回的数据组装成一个NetworkResponse对象进行返回。在NetworkDispatcher中收到了NetworkResponse这个返回值后又会调用Request的parseNetworkResponse()方法来解析NetworkResponse中的数据,同时将数据写入到缓存,这个方法的实现是交给Request的子类来完成的,因为不同种类的Request解析的方式也肯定不同。



前面你可以看到在NetWorkDispatcher的run中最后执行了`mDelivery.postResponse(request, response);`,也就是说在解析完了NetworkResponse中的数据之后,又会调用ExecutorDelivery(ResponseDelivery接口的实现类)的postResponse()方法来回调解析出的数据,具体代码如下所示:



@Override

public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {

request.markDelivered();

request.addMarker("post-response");

mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));

}




这里可以看见在mResponsePoster的execute()方法中传入了一个ResponseDeliveryRunnable对象,就可以保证该对象中的run()方法就是在主线程当中运行的了,我们看下run()方法中的代码是什么样的:



/**

  • A Runnable used for delivering network responses to a listener on the

  • main thread.

*/

@SuppressWarnings(“rawtypes”)

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();

    }

}

}




这段代码里的run方法中可以看到如下一部分细节:



// Deliver a normal response or error, depending.

if (mResponse.isSuccess()) {

mRequest.deliverResponse(mResponse.result);

} else {

mRequest.deliverError(mResponse.error);

}




这段代码是最核心的,明显可以看到通过mRequest的deliverResponse或者deliverError将反馈发送到回调到UI线程。这也是你重写实现的接口方法。



[]( )再来整体看下

-------------------------------------------------------------------



现在是该回过头去看背景知识模块了,再看下那幅官方图,对比就明白咋回事了。结合上图和如上分析可以知道:



1.  当一个RequestQueue被成功申请后会开启一个CacheDispatcher和4个默认的NetworkDispatcher。

    

2.  CacheDispatcher缓存调度器最为第一层缓冲,开始工作后阻塞的从缓存序列mCacheQueue中取得请求;对于已经取消的请求,标记为跳过并结束这个请求;新的或者过期的请求,直接放入mNetworkQueue中由N个NetworkDispatcher进行处理;已获得缓存信息(网络应答)却没有过期的请求,由Request的parseNetworkResponse进行解析,从而确定此应答是否成功。然后将请求和应答交由Delivery分发者进行处理,如果需要更新缓存那么该请求还会被放入mNetworkQueue中。

    

3.  将请求Request add到RequestQueue后对于不需要缓存的请求(需要额外设置,默认是需要缓存)直接丢入mNetworkQueue交给N个NetworkDispatcher处理;对于需要缓存的,新的请求加到mCacheQueue中给CacheDispatcher处理;需要缓存,但是缓存列表中已经存在了相同URL的请求,放在mWaitingQueue中做暂时处理,等待之前请求完毕后,再重新添加到mCacheQueue中。

    

4.  网络请求调度器NetworkDispatcher作为网络请求真实发生的地方,对消息交给BasicNetwork进行处理,同样的,请求和结果都交由Delivery分发者进行处理。

    

5.  Delivery分发者实际上已经是对网络请求处理的最后一层了,在Delivery对请求处理之前,Request已经对网络应答进行过解析,此时应答成功与否已经设定;而后Delivery根据请求所获得的应答情况做不同处理;若应答成功,则触发deliverResponse方法,最终会触发开发者为Request设定的Listener;若应答失败,则触发deliverError方法,最终会触发开发者为Request设定的ErrorListener;处理完后,一个Request的生命周期就结束了,Delivery会调用Request的finish操作,将其从mRequestQueue中移除,与此同时,如果等待列表中存在相同URL的请求,则会将剩余的层级请求全部丢入mCacheQueue交由CacheDispatcher进行处理。

    



至此所有搞定。



PPPS一句:通过上面原理分析之后总结发现,推荐整个App全局持有一个RequestQueue的做法,这样会有相对比较高的性能效率。



文章转自:http://blog.youkuaiyun.com/yanbober/article/details/45307217



感谢博主的无私分享!



下面两篇文章也写得不错,也是剖析volley框架及源码



http://www.cnblogs.com/cpacm/p/4211719.html  



### 最后

**Java架构进阶面试及知识点文档笔记**

> **这份文档共498页,其中包括Java集合,并发编程,JVM,Dubbo,Redis,Spring全家桶,MySQL,Kafka等面试解析及知识点整理**

![image](https://img-blog.csdnimg.cn/img_convert/5d28b09939ac8256871d02c9fbf65755.webp?x-oss-process=image/format,png)

**Java分布式高级面试问题解析文档**

**其中都是包括分布式的面试问题解析,内容有**分布式消息队列,Redis缓存,分库分表,微服务架构,分布式高可用,读写分离等等!

![image](https://img-blog.csdnimg.cn/img_convert/51eadf840c22402f47ceefc8a01ffb7d.webp?x-oss-process=image/format,png)

**互联网Java程序员面试必备问题解析及文档学习笔记**

![image](https://img-blog.csdnimg.cn/img_convert/1510302c6709eb7e52938c39b1542fe1.webp?x-oss-process=image/format,png)

**Java架构进阶视频解析合集**


句:通过上面原理分析之后总结发现,推荐整个App全局持有一个RequestQueue的做法,这样会有相对比较高的性能效率。



文章转自:http://blog.youkuaiyun.com/yanbober/article/details/45307217



感谢博主的无私分享!



下面两篇文章也写得不错,也是剖析volley框架及源码



http://www.cnblogs.com/cpacm/p/4211719.html  



### 最后

**Java架构进阶面试及知识点文档笔记**

> **这份文档共498页,其中包括Java集合,并发编程,JVM,Dubbo,Redis,Spring全家桶,MySQL,Kafka等面试解析及知识点整理**

[外链图片转存中...(img-O4NKOVPK-1714504417948)]

**Java分布式高级面试问题解析文档**

**其中都是包括分布式的面试问题解析,内容有**分布式消息队列,Redis缓存,分库分表,微服务架构,分布式高可用,读写分离等等!

[外链图片转存中...(img-SUQpqLBH-1714504417949)]

**互联网Java程序员面试必备问题解析及文档学习笔记**

[外链图片转存中...(img-uy3A4OZb-1714504417949)]

**Java架构进阶视频解析合集**


> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.youkuaiyun.com/topics/618154847)收录**
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值