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

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. MVC应用程序实现上传文件(续)

    前几天,有练习了<MVC应用程序实现上传文件>http://www.cnblogs.com/insus/p/3590907.html 那只是把文档上传至MVC应用程序下的某一目录之中. 其 ...

  2. C++中的 CONST 含义(从#define 到 CONST 的转变)

    const 与define 两者都可以用来定义常量,但是const定义时,定义了常量的类型,所以更精确一些.#define只是简单的文本替换,除了可以定义常量外,还可以用来定义一些简单的函数,有点类似 ...

  3. java中带参数的try(){}语法

    带资源的try语句(try-with-resource)的最简形式为: try(Resource res = xxx)//可指定多个资源 { work with res } try块退出时,会自动调用 ...

  4. sql 查询结果作为数据进行添加,where in 子查询

    查询结果作为数据进行添加 INSERT INTO a ( Aid, Atitle, Url, Pic1 ) SELECT c Aid,d Atitle,e Url,f Pic1 FROM b 对于大神 ...

  5. FineUI 布局宽度自适应,后台回调js方法

    FineUI页面布局,宽度自适应 @(F.Panel().CssClass().ShowBorder().BoxConfigChildMargin("0 5 0 0").ShowH ...

  6. Java虚拟机 - 类加载机制

    [深入Java虚拟机]之四:类加载机制 类加载过程     类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载.验证.准备.解析.初始化.使用和卸载七个阶段.它们开始的顺序如下 ...

  7. SqlSession对象之ParameterHandler

    上一篇讲了StatementHandler,其中有ParameterHandler(参数处理器)是在StatementHandler被创建时被创建的.下面对ParameterHandler进行说明.其 ...

  8. PHP-隐藏手机号中间四位

    substr_replace('手机号', '****', 3, 4);

  9. OSGI企业应用开发(九)整合Spring和Mybatis框架(二)

    上篇文章中,我们完成了在OSGI应用中整合Spring和Mybatis框架的准备工作,本节我们继续Spring和Mybatis框架的整合. 一.解决OSGI整合Spring中的Placeholder问 ...

  10. GDAL打开HDF格式时遇到的中文路径问题(未解决)

    众所周知,中文环境下(VS2010 C++工程编码为多字节编码),在使用1.8.0版本以后的GDAL打开中文路径下的影像文件(如GeoTiff文件)时, 需对中文文件路径做特殊处理,有2种方法:(我使 ...