这篇文章主要分析网络请求和结果交付的过程。

NetWork工作原理

之前已经说到通过mNetWork.performRequest()方法来得到NetResponse,看一下该方法具体的执行流程,performRequest是一个接口方法,真正实现该方法以及被调用的是BasicNetWork,其具体的performRequest代码如下:

 @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;
if (httpResponse != null) {
statusCode = httpResponse.getStatusLine().getStatusCode();
} else {
throw new NoConnectionError(e);
}
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
NetworkResponse networkResponse;
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 if (statusCode >= 400 && statusCode <= 499) {
// Don't retry other client errors.
throw new ClientError(networkResponse);
} else if (statusCode >= 500 && statusCode <= 599) {
if (request.shouldRetryServerErrors()) {
attemptRetryOnException("server",
request, new ServerError(networkResponse));
} else {
throw new ServerError(networkResponse);
}
} else {
// 3xx? No reason to retry.
throw new ServerError(networkResponse);
}
} else {
attemptRetryOnException("network", request, new NetworkError());
}
}
}
}

上述代码可以看出,真正进行网络请求还是HttpStack.performRequest,在请求结束以后,对返回的状态码进行封装,这里主要说一下304状态码,该状态码,由于只返回header,不返回body,因此body需要从以前缓存的entity中取出body。而HttpStack的两个实现类一个是HttpClientStack,另一个是HurlStack,他们也是直接调用Java自带的http请求方法来得到response。

下面要看的是NetWorkResponse的封装

    public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
boolean notModified, long networkTimeMs) {
this.statusCode = statusCode;
this.data = data;
this.headers = headers;
this.notModified = notModified;
this.networkTimeMs = networkTimeMs;
}

这就是全部的NetWorkResponse的封装,封装的目的是为了对response进行交付。

ResponseDelivery工作原理

ResponseDelivery是一个接口,其定义了3个方法,2个传递response的重载方法与一个传递error方法

 /**
* Parses a response from the network or cache and delivers it.
*/
void postResponse(Request<?> request, Response<?> response); /**
* Parses a response from the network or cache and delivers it. The provided
* Runnable will be executed after delivery.
*/
void postResponse(Request<?> request, Response<?> response, Runnable runnable); /**
* Posts an error for the given request.
*/
void postError(Request<?> request, VolleyError error);

其实现类ExecutorDelivery是真正执行的功能类。其内部非常简单,代码如下:

public class ExecutorDelivery implements ResponseDelivery {
/** Used for posting responses, typically to the main thread. */
private final Executor mResponsePoster; /**
* Creates a new response delivery interface.
* @param handler {@link Handler} to post responses on
*/
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);
}
};
} /**
* Creates a new response delivery interface, mockable version
* for testing.
* @param executor For running delivery tasks
*/
public ExecutorDelivery(Executor executor) {
mResponsePoster = executor;
} @Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
} @Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
} @Override
public void postError(Request<?> request, VolleyError error) {
request.addMarker("post-error");
Response<?> response = Response.error(error);
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
} /**
* 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();
}
}
}
}

其构造方法唯一目的就是实例化了Excutor类型的mResponsePoster,Volley默认调用的是第一个构造方法,也就是包装了一个Handler.该类最主要的方法是postResponse,其核心的实现

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

执行的是ResponseDeliveryRunnable的run方法,而run方法中是通过

mRequest.deliverResponse(mResponse.result);

而Request的该方法则是由有其具体的实现类 StringRequest,JsonRequest,ImageRequest实现,其实现都一致,则是

    @Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}

调用回调方法。

因此,整个分发过程就清晰了,通过Dispatcher进行处理的每个Request,对request进行处理完,则通过ResponseDelivery进行交付,其交付通过得到一个UI线程的handler,通过该Handler的post,调用Request类的delivery方法,在该方法中,又会调用我们构造request时,传入的回调方法。

Volley源码分析(四)NetWork与ResponseDelivery工作原理的更多相关文章

  1. Vue.js 源码分析(四) 基础篇 响应式原理 data属性

    官网对data属性的介绍如下: 意思就是:data保存着Vue实例里用到的数据,Vue会修改data里的每个属性的访问控制器属性,当访问每个属性时会访问对应的get方法,修改属性时会执行对应的set方 ...

  2. zuul源码分析-探究原生zuul的工作原理

    前提 最近在项目中使用了SpringCloud,基于zuul搭建了一个提供加解密.鉴权等功能的网关服务.鉴于之前没怎么使用过Zuul,于是顺便仔细阅读了它的源码.实际上,zuul原来提供的功能是很单一 ...

  3. [ipsec][strongswan] strongswan源码分析--(四)plugin加载优先级原理

    前言 如前所述, 我们知道,strongswan以插件功能来提供各种各样的功能.插件之间彼此相互提供功能,同时也有可能提供重复的功能. 这个时候,便需要一个优先级关系,来保证先后加载顺序. 方法 在配 ...

  4. Volley 源码分析

    Volley 源码分析 图片分析 要说源码分析,我们得先看一下官方的配图: 从这张图中我们可以了解到 volley 工作流程: 1.请求加入优先队列 2.从缓存调度器中查看是否存在该请求,如果有(没有 ...

  5. Volley源码分析一

    Volley源码分析 虽然在2017年,volley已经是一个逐渐被淘汰的框架,但其代码短小精悍,网络架构设计巧妙,还是有很多值得学习的地方. 第一篇文章,分析了请求队列的代码,请求队列也是我们使用V ...

  6. Volley源码分析(一)RequestQueue分析

    Volley源码分析 虽然在2017年,volley已经是一个逐渐被淘汰的框架,但其代码短小精悍,网络架构设计巧妙,还是有很多值得学习的地方. 第一篇文章,分析了请求队列的代码,请求队列也是我们使用V ...

  7. Volley源码分析(2)----ImageLoader

    一:imageLoader 先来看看如何使用imageloader: public void showImg(View view){ ImageView imageView = (ImageView) ...

  8. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  9. Spring Ioc源码分析系列--@Autowired注解的实现原理

    Spring Ioc源码分析系列--@Autowired注解的实现原理 前言 前面系列文章分析了一把Spring Ioc的源码,是不是云里雾里,感觉并没有跟实际开发搭上半毛钱关系?看了一遍下来,对我的 ...

随机推荐

  1. C++11 Lambda表达汇总总结

    C++ 11中的Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作.Lambda的语法形式如下:               [函数对象参数] (操作符重载函数参数) mutable或ex ...

  2. 【移入移出事件练习】【菜单】【选项卡】 -------this使用

    鼠标移入移出事件练习 建一个长100x100的红色 div,鼠标移入变为200x200绿色 .a { width:100px; height:100px; background-color:red ; ...

  3. JSP与Servlet之间传值的一般方法,适合新手做练习

    #JSP与Servlet之间传值 Jsp与Servlet之间的传值有两种,一种是Jsp传值给Sevlet,另一种是Servlet传值给Jsp:使用request.response对象完成传值,具体实现 ...

  4. Web前端基础——CSS

    一.CSS概述 css ( cascading style sheets ) 层叠样式表,可以轻松设置网页元素的显示.位置和格式外,甚至还能产生滤镜,图像 淡化,网页淡入淡出的渐变效果,简而言之,cs ...

  5. git 撤销add和commit

    有时候改完代码发现改错分支了,而这个时候已经add或者commit了,怎么办,有办法: 1.若果已经add .  了这个时候可以使用git stash命令,具体操作命令如下: (1) > git ...

  6. 【转载】Nginx+Tomcat 动静分离实现负载均衡

    0.前期准备 使用Debian环境.安装Nginx(默认安装),一个web项目,安装tomcat(默认安装)等. 1.一份Nginx.conf配置文件 1 # 定义Nginx运行的用户 和 用户组 如 ...

  7. git使用基本教程

    黑马的视频,以前看过廖雪峰的git,总是学不懂,这次终于看会了,结合视频更佳,红色字是重点. 基于linux下面git百度云视频教程:http://pan.baidu.com/s/1bpk472B 密 ...

  8. Django-url反向解析和命名空间

    一.urls硬编码 在反向解析和命名空间之前我们先来说说URLS硬编码,用django 开发应用的时候,可以完全是在urls.py 中硬编码配置地址,在views.py中HttpResponseRed ...

  9. tensorflow模型的保存与恢复

    1.tensorflow中模型的保存 创建tf.train.saver,使用saver进行保存: saver = tf.train.Saver() saver.save(sess, './traine ...

  10. 鼠标滚轮更改transform的值(vue-scroller在PC端的上下滑动)

    目前上拉刷新,下拉加载,以及区域回弹的组件,绝大多数都是通过transform去实现的.在移动端效果很好,但是PC端使用鼠标拖拽的方式,查看下文首先不符合逻辑,其次容易点进其他页面. 起初,项目的初衷 ...