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

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. WPF备忘录(5)怎样修改模板中的控件

    首先,想问大家一个问题,你们如果要给一个Button添加背景图片会怎么做?(呵呵,这个问题又点小白哈) 是这样吗? <Button Height="57" Horizonta ...

  2. Winsock API TCP/IP网络通信

    通信流程如下: 1.Winsock库的装入.初始化 #pragma comment(lib,"WS2_32.lib").WSAStartup() 2.套接字的创建(服务器端是监听套 ...

  3. [EWS]查找 文件夹

    摘要 有时在操作exchange的时候,需要查找用户exchange文件夹,比如用户新建了一些文件夹. 一个例子 这里以查找用户outlook邮箱中的历史对话文件夹为例. private const ...

  4. Java并发编程-Executor框架集

    Executor框架集对线程调度进行了封装,将任务提交和任务执行解耦. 它提供了线程生命周期调度的所有方法,大大简化了线程调度和同步的门槛. Executor框架集的核心类图如下: 从上往下,可以很清 ...

  5. Java基础——GUI编程(二)

    一.事件监听机制 -- 事件源:awt 或swing包中的那些图形界面组件,即发生事件的组件 -- 事件:Event  用户对组件的一个操作 -- 监听器:Listener  负责处理事件的方法 二. ...

  6. 【JVM】1、java虚拟机参数-X 与 -XX的区别

    Options that begin with -X are non-standard (not guaranteed to be supported on all VM implementation ...

  7. 图像阈值化-threshold、adaptivethreshold

    在图像处理中阈值化操作,从一副图像中利用阈值分割出我们需要的物体部分(当然这里的物体可以是一部分或者整体).这样的图像分割方法是基于图像中物体与背景之间的灰度差异,而且此分割属于像素级的分割.open ...

  8. 解决stackoverflow打开缓慢的问题

    一.原因: 因为stackoverflow用的是谷歌的api,在国内谷歌是被禁用的,所以才会打开缓慢,并不是stackverflow被墙 二.解决方法: 1.如果你正在使用的是火狐浏览器,那么请按照下 ...

  9. PeopleSoft面试题...

    Q1:PS发出的邮件附件名字中中文字符乱码在哪设置? A1: 分为APP和PROCESS两个配置文件,分别在psprcs.cfg 和 psappsrv.cfg 中 SMTP Settings设置. 评 ...

  10. 安装ArcGIS Enterprise WebGIS (Portal ArcGIS Server DataStore ) 系统后如何应对网络环境的配置修改

    客户往往在部署完ArcGIS WebGIS系统后,由于需要满足业务或者网络管理的要求,需要修改系统的网络环境的配置,下文将从常见的几个场景来讲述如何去应对这些变动. 1.网络IP地址变动 由于在部署W ...