本文是对Volley思路的整体整理,并不是Volley教程,建议有Volley使用经验,但是对Volley整体不是很清楚的同学阅读。

我认为,弄清整体的流程很重要,以避免一叶障目不见泰山的囧境,而对于面向对象编程,弄清每个类是干什么的,类与类之间的关系后,就不难搞懂整个流程了。

所以本文不会深入源码细节,从每个类的构造参数最全的构造函数入手,讲解这个类是干什么的,由什么构成,每个元素的作用是什么。

因为不会深入细节,所以建议最好了解Volley的基本使用方法。

1.Volley类初始化RequestQueue

在整个Volley流程里,原始类是Volley,这个类中有一个静态方法会new一个RequestQueue,而一切都从这里开始


public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
} Network network = new BasicNetwork(stack);
RequestQueue queue;
if (maxDiskCacheBytes <= -1)
{
// No maximum size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
}
else
{
// Disk cache size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
}
queue.start();
return queue;
}

RequestQueue

可以看到我们首先根据SDK版本来选择合适的HTTP请求方案,版本大于等于9的时候使用HTTPUrlConnection进行请求。

根据你请求方案来初始化我们的RequestQueue,看一下RequestQueue的构造:


public RequestQueue(Cache cache,
Network network,
int threadPoolSize,
ResponseDelivery delivery) {/*...*/}

四个参数:缓存,请求方案,网络连接池大小,请求结果分发器

缓存:Volley默认使用DiskBasedCache

请求方案:HttpClient和HttpUrlConnection两种

网络连接池:即建立几个网络请求线程NetWorkDispatchThread,默认的大小是4

请求结果分发器:当我们获得请求结果后,有它分发出去,通常回调我们之前定义的处理方法。

剩下的细节暂且不表,建立了RequestQueue后,我们会获得两个队列,缓存请求队列和网络请求队列,那由谁来到队列里取出请求呢?

2.建立线程处理请求队列,两种请求对应两种线程


public CacheDispatcher(BlockingQueue<Request<?>> cacheQueue,
BlockingQueue<Request<?>> networkQueue,
Cache cache, ResponseDelivery delivery) {/*...*/} public NetworkDispatcher(BlockingQueue<Request<?>> queue,
Network network,
Cache cache,
ResponseDelivery delivery){/*...*/}

CahceDispathcher,即缓存请求处理线程,四个初始化参数,这里看到第二个参数是:networkQueue。咦?既然是处理缓存请求的线程,为什么要在这里传入网络请求队列呢?

从cachequeue中拿到请求,根据请求信息去chche中获取,如果缓存没问题,则通过delivery分发,如果我们会遇到缓存过期,或者缓存没过期但是需要更新的情况,这个时候就需要把请求放入networkqueue中,以供下一步请求。

NetworkDispatcher,即网络请求处理线程,也是四个初始化参数:从queue中获得请求,有network方案执行请求,返回结果后如果可以缓存则放入cache,最后将返回的结果通过delivery送到处理的地方。

3.由HttpStack进行网络工作

cache部分就像上面说的那样,最后回到网络部分,这部分请求工作就需要单独说一说了。


public interface HttpStack {
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
} public HttpClientStack(HttpClient client) {/*...*/} public HurlStack(UrlRewriter urlRewriter,
SSLSocketFactory sslSocketFactory){/*...*/}

在HttpStack接口中,只有一个要实现的方法:performRequest。顾名思义,就是执行请求的意思,我们所有的Request都要实现这个方法,根据请求的不同网络请求执行过程有很大不同。

有两个参数,一个是请求本身,还有一个是附加的头信息,在Volley中,根据不同的请求阶段和请求状态,我们会定义不同的附加说明信息,这些信息会放在header里。

如上文所说,根据SDK版本不用,我们使用不同的请求方案,在这里对应的就是HttpClentStack和HurlStack了。

剩下的就是android的网络请求知识,在本篇文章中不是重点。

总之最后我们会拿到请求的回应response,而在不同的请求中,由于数据格式不同,我们需要将response返回的二进制字节流转化成需要的格式,

这时候就要调用resquest的 request.parseNetworkResponse(networkResponse)方法,解析response,按照要求解析之后,我们拿到的才是自习想用的数据,如:BitMap、Json等等,这才是我们真正需要要的Response,然后呢?

4.Delivery将Response运走


public interface ResponseDelivery { public void postResponse(Request<?> request, Response<?> response);
/**
* 解析从网络或者缓存中获取的响应并分发出去。
* 所定义的的线程将在分发后执行
*/
public void postResponse(Request<?> request, Response<?> response, Runnable runnable); /**
* Posts an error for the given request.
*/
public void postError(Request<?> request, VolleyError error);
}

在其具体实现类ExecutorDelivery中,无论如何都会执行runnable,即使初始化为null。因为我们将最后的结果处理统一放在了那个线程里,简化版源码如下所示


public class ExecutorDelivery implements ResponseDelivery { 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);
}
};
} @Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
} private class ResponseDeliveryRunnable implements Runnable { public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
//...
} public void run() {
//...
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
//...
// If we have been provided a post-delivery runnable, run it.
if (mRunnable != null) {
mRunnable.run();
}
}
}
}

可以看到,最后调用的是mRequest.deliverResponse,这个才是处理了最后求的方法,所以,我们需要关注一下这些请求本身了。

5.各种请求

上面就是大概的请求分发过程,至于具体的处理请求方法就涉及到HTTP的知识了,此处都不细讲,过两天会专门结合HTPP语义和Volley来写一篇文章。

Volley有一大特性就是灵活,因为Volley设计中有一个很重要的思想是“面向接口编程”,我们可以定制符合自己要求的Request,Request接口中这样初始化一个Request:


public Request(int method,
String url,
Response.ErrorListener listener){/*...*/}

三个参数:请求方法(GET、PUT、POST…),请求的URL,请求错误的监听器。

也就是说,我们至少要让自己的请求具备这个三个参数,剩下的就随意我们扩展了,而Volley也为大家提供了几种定制好的请求,下面就来看一看吧。


public ImageRequest(String url,
Response.Listener<Bitmap> listener,
int maxWidth,
int maxHeight,
ScaleType scaleType,
Config decodeConfig,
Response.ErrorListener errorListener) {/*...*/} public JsonArrayRequest(String url,
Listener<JSONArray> listener,
ErrorListener errorListener) {/*...*/} public JsonObjectRequest(int method,
String url,
JSONObject jsonRequest,
Listener<JSONObject> listener,
ErrorListener errorListener) {/***/} public StringRequest(int method,
String url,
Listener<String> listener,
ErrorListener errorListener) {/*...*/} public JsonRequest(int method,
String url,
String requestBody,
Listener<T> listener,
ErrorListener errorListener){/*...*/}

最复杂的就是ImageRequest,这里初始化没有method,因为默认是使用GET方法的,随后定义了结果监听器,最大宽度,最大高度,缩放方式,解码配置,错误监听,这个就涉及到在Android中加载图片需要注意的事项了,也会写一篇单独的文章讲一下。

后面的几种,他们的参数顾名思义就可以了,哈哈,我就偷个懒,不讲了。

为什么要单独将这些请求列出来呢?

因为有一个很重要的参数,这个参数的存在,将Volley整个流程形成的闭环,那就是,上述所有构造器中都有的一个参数: Lister<T> listener (T表示请求数据的类型)

这个Listener就用来接收delievery分发回来的结果数据,对数据进行处理,这个listener需要由我们自己重写,按照想要的方式展示结果数据。

在第4小节中提到了mRequest.deliverResponse的方法,此时可以看看它的真面目了,此处以ImageQequest为例:


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

这回大家都明白了吧。

一切都自Request始,到这里又有request终。

回过头来一看,是这样的一个过程:


START 1.定义你的Request,并定义好相应请求结果的回调Listener 2.建立RequestQueue 3.将你的Request放入RequestQueue中(RequestQueue.add()) 4.由cacheDispatcher和networkDispatcher线程进行请求的分发 5.按照请求的性质,有cache或者HttpStack进行处理请求,获取返回的数据 6.处理返回数据,该放入缓存的放入缓存,然后使用ExecutorDelivery将结果分发出去。 7.调用Request的Listener,接受并处理Delivery给我们分发回来的结果。 END

本文尽量从一个清晰的思路讲解Volley的流程,从中体会Volley的设计思想。

为了清晰简洁,除了ExecutorDelivery以为,并没有深究细节,但是希望看完后可以去仔细研究Volley的细节,因为不研究细节是无法总结出来整体流程的。我也是翻来覆去看了几遍源码,才总结出来的这篇文章。

有浅入深,而后深入浅出。

这是学习Volley的过程中最大的心得,Volley并不难懂,难的是将从Volley学到思想用在自己的程序中,这就需要多多练习了。

如果有错误的地方,麻烦大家指正。

如需转载,请注明原作:

Volley设计思想和流程分析的更多相关文章

  1. Day1:了解APICloud平台、理解APICloud应用设计思想、掌握平台使用流程。学习如何对一款APP进行需求分析、功能分解和架构设计等编码之前重要的准备工作

    学习目标 总体上了解一下APICloud平台,重点介绍相关的学习资源,入门资料,常见的FAQ等 明确我们这七天要开发一个什么样的APP,明确功能需求,跟上每天的课程节奏,可以课前预习 梳理出对于一款A ...

  2. C++STL内存配置的设计思想与关键源码分析

    说明:我认为要读懂STL中allocator部分的源码,并汲取它的思想,至少以下几点知识你要了解:operator new和operator delete.handler函数以及一点模板知识.否则,下 ...

  3. 揭秘jbpm流程引擎内核设计思想及构架

    揭秘jbpm流程引擎内核设计思想及构架 作者 胡长城(银狐999)   1     前言 2     阅读本篇的基础准备 2.1      概念的基础 2.2      环境的基础 3     什么是 ...

  4. 02-FPGA设计流程介绍——小梅哥FPGA设计思想与验证方法视频教程配套文档

    芯航线——普利斯队长精心奉献 课程目标: 1.了解并学会FPGA开发设计的整体流程 2.设计一个二选一选择器并进行功能仿真.时序仿真以及板级验证 实验平台:芯航线FPGA开发板.杜邦线 实验内容: 良 ...

  5. 撰写一篇博客要求讲述四则运算2的设计思想,源程序代码、运行结果截图、编程总结分析,并按照PSP0级的要求记录开发过程中的时间记录日志。

    一.撰写一篇博客要求讲述四则运算2的设计思想,源程序代码.运行结果截图.编程总结分析,并按照PSP0级的要求记录开发过程中的时间记录日志. 1.设计思想: ①创建test.jsp建立第一个前端界面,提 ...

  6. Spring5源码分析(1)设计思想与结构

    1 源码地址(带有中文注解)git@github.com:yakax/spring-framework-5.0.2.RELEASE--.git Spring 的设计初衷其实就是为了简化我们的开发 基于 ...

  7. AOP设计思想_开发流程

    程序员一直在努力做一件事请,写更少的代码,做更多的事情,提高开发效率 在一个开发团队里面,一个人最多只做一件事情,绝对不会说,刚接手做了没多久的任务,上头又交给你另一项任务,绝对不会有的 下面,梦逸来 ...

  8. spring事务管理器设计思想(二)

    上文见<spring事务管理器设计思想(一)> 对于第二个问题,涉及到事务的传播级别,定义如下: PROPAGATION_REQUIRED-- 如果当前没有事务,就新建一个事务.这是最常见 ...

  9. CEPH浅析”系列之三——CEPH的设计思想

    Ceph针对的目标应用场景 理解Ceph的设计思想,首先还是要了解Sage设计Ceph时所针对的目标应用场景,换言之,"做这东西的目的是啥?" 事实上,Ceph最初针对的目标应用场 ...

随机推荐

  1. iOS10 权限适配

    权限适配 这应该算iOS10系统适配的范畴,最近这两个都在弄,所以就直接和Xcode8适配一起写出来了. 在iOS10之后需要在Info.plist中,添加新的字段获取权限,否则在iOS10上运行会导 ...

  2. 【JAVA】【Eclipse】出现This element neither has attached source nor attached Javadoc...的解决方法

    This element neither has attached source nor attached Javadoc and hence no Javadoc could be found Ec ...

  3. MySQL 第十天(视图、存储过程、函数、触发器)

    MySql高级-视图.函数.存储过程.触发器 目录 一.视图    1 1.视图的定义    1 2.视图的作用    1 (1)可以简化查询.    1 (2)可以进行权限控制,    3 3.查询 ...

  4. 怎么解决svn清理失败且路径显示乱码问题

    http://jingyan.baidu.com/article/295430f1d728830c7e0050f9.html 上面这个网址是百度经验给的方法,我也是参照这个方式解决了问题,虽然是解决了 ...

  5. vs2013密钥

    Visual Studio Ultimate 2013 KEY(密钥):BWG7X-J98B3-W34RT-33B3R-JVYW9 Visual Studio Premium 2013 KEY(密钥) ...

  6. XP局域网内专用消息队列

    网上能找到DELPHI消息队列的方法,在XP下试了总是不成功,后来在2003上试就行了,对比发现消息队列属性->安全 2003中多了个用户ANONYMOUS_LOGON. 然后在XP下消息队列属 ...

  7. Ubuntu 14.10安装SecureCRT 7.3(转)

    原文 :http://blog.csdn.net/chszs/article/details/40623169 1.软件准备 Ubuntu14.04 x64 下载SecureCRT7.3的版本:scr ...

  8. 45、Docker 加 tensorflow的机器学习入门初步

    [1]最近领导天天在群里发一些机器学习的链接,搞得好像我们真的要搞机器学习似的,吃瓜群众感觉好神奇呀. 第一步 其实也是最后一步,就是网上百度一下,Docker Toolbox,下载下来,下载,安装之 ...

  9. EM算法(3):EM算法运用

    目录 EM算法(1):K-means 算法 EM算法(2):GMM训练算法 EM算法(3):EM算法运用 EM算法(4):EM算法证明 EM算法(3):EM算法运用 1. 内容 EM算法全称为 Exp ...

  10. 26 Time Management(转)

    01. There is alway time. Time is priorities. 时间常有.时间优先. 02. Days always fill up. 时间总会有的. Only plan f ...