本文是对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学到思想用在自己的程序中,这就需要多多练习了。

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

如需转载,请注明原作:

最新文章

  1. ABP文档 - Mvc 控制器
  2. Ubuntu环境变量(.profile)加载顺序
  3. 1Z0-053 争议题目解析685
  4. 【Beta】Scrum05
  5. MySQL数据库1 - 基本概念及安装
  6. this 关键字
  7. include a image in devexpress datagrid
  8. Linux2.6内核--内存管理(1)--分页机制
  9. jQuery的事件和动画
  10. [置顶] 自己动手写Web容器之TomJetty之六:动态页面引入
  11. Struts ActionForm简单理解
  12. Android项目---listview的那些属性,常用却不常见
  13. MongoDB索引(一)
  14. HDU 3689 Infinite monkey theorem [KMP DP]
  15. 【刷题】【LeetCode】总
  16. 一文入门C3
  17. jsp基础语言-jsp注释
  18. MySQL根据某个字段查询重复的数据
  19. Java Bean的规范
  20. day14: 生成器进阶

热门文章

  1. Linux CentOS安装postgresql 9.4
  2. function语句和function表达式的随笔
  3. 正确理解ThreadLocal
  4. Android IOS WebRTC 音视频开发总结(八十五)-- 使用WebRTC广播网络摄像头视频(下)
  5. 3o_AutoSSH
  6. Android 打包
  7. js实现继承的两种方式
  8. NSSortDescriptor对象进行数组排序
  9. ODAC ,MYDAC版本问题
  10. vim中替换命令