本文是对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. [转载:]C#与Fortran混合编程之本地调用Fortran动态链接库

    前言 C#发展到现在,已是一门相当完善的语言,他基于C语言风格,演化于C++.并依靠强大的.NET底层框架.C#可以用来快速构建桌面及Web应用.然而在我们的实际工作中,尽管C#已经非常完善,但还是不 ...

  2. nodejs+express+jade配置

    安装步骤 一.首先可跟着这个网址安装http://jingyan.baidu.com/article/91f5db1b2bb6941c7f05e33c.html,路径可由自己定. 二.同时参考http ...

  3. Windows转到linux中,文件乱码,文件编码转换 & 解决sqlplus连接oracle乱码

    转载:http://www.cnblogs.com/wanyao/p/3399269.html 最近,学习又重新开始Linux学习,所以一直在Centos中,昨天一朋友把他在Windows下写的C程序 ...

  4. HTTP权威指南笔记-2.URL与资源

    2.1 URI与URL.URN URL是浏览器寻找信息时所需的具体位置.URl是人们对HTTP和其他协议的常用访问点:浏览器指向一个URL,浏览器就会发送适当的协议报文向服务器获取内容. URI是一类 ...

  5. 解决phalcon读取mysql乱码

    原先的项目用的是phalcon,迁移到新服务器上面后中文字符变为'?',即便连接参数设置了charset => 'utf8'也无效,一开始怀疑是版本问题,后来直接拷过去完全一样的库也没用:最后还 ...

  6. CSS中如何让元素隐藏

    在CSS中,让元素隐藏(指屏幕范围内肉眼不可见)的方法很多,有的占据空间,有的不占据空间:有的可以响应点击,有的不能响应点击.下面一个个列出,选一个适合你的 { display: none; /* 不 ...

  7. 将复杂查询写到SQL配置文件--SOD框架的SQL-MAP技术简介

    引言 今天看到一片热门的博客, .NET高级工程师面试题之SQL篇 ,要求找出每一个系的最高分,并且按系编号,学生编号升序排列.这个查询比较复杂,也比较典型,自从用了ORM后,很久没有写过SQL语句了 ...

  8. window.print() 去掉页眉页脚及打印链接【转载】

    页面中添加样式: <style media="print"> @page { size: auto; /* auto is the initial value */ m ...

  9. Android开发环境

    1: JDK 2: Eclipse 3: Android SDK 4: ADT

  10. 如何在HTML中加载Flash(2种实现方法)_HTML/Xhtml_网页制作

    点评:如何在HTML中加载Flash,为网页添加更多的色彩,普通的网页以无法满足用户的需求,接下来为大家介绍下2种在HTML中加载Flash的方法,感兴趣的各位可以适当参考下,希望对你有所帮助 第一种 ...