前面三篇文章从最基础的TCP,HTTP协议理论开始,然后介绍了在Android的开发中所使用的HttpClient和HttpUrlConnection这两种Http客户端。在本文中,我们一起来学习一下在Android开发中经常使用的volley框架。首先,我们会从架构的角度了解一下整个框架的结构,然后从源码的角度理解框架实现细节。

volley是Google在13年发布的一款Android异步网络请求框架。volley有着鲜明的特点:适用于小数据量且频繁的网络请求。这个特点特别适合于Android应用程序的网络操作。另外,我们从接下来将要介绍的架构中可以发现,volley采用了大量面向接口的设计,保证了整个框架的开放性和灵活性,可以根据不同的情况需求进行定制。同时,volley也提供了简单的图片加载工具。

在我们研究volley框架的结构之前,我们不妨自己先来思考一下如何实现一款网络操作的框架。首先,我们根据实际需求设计网络操作请求(无非就是URL及其参数),然后使用多线程并发来执行和处理网络操作请求任务。从服务器中取到数据后,将数据放在内存的cache中以便应用程序使用。

其实volley框架也是如此设计的,如下图所示:

从图中我们可以看到,整个框架的结构分为4个部分:

1、volley使用一个请求队列来管理各种网络请求Request。Request本身是一个描述请求的抽象类,我们可以根据具体需求情况实现该抽象类。volley也提供了一些Request子类,例如StringRequest,JsonRequest,ImageRequest等等。

2、在请求队列RequestQueue中,有两种个轮询分发线程负责对请求任务进行分发调度。第一种是CacheDispatcher,负责调度数据保存在cache中的请求任务;第二种是NetworkDispatcher,负责调度数据在远端服务器上的请求任务。另外,RequestQueue中还有一个叫作ResponseDelivery的接口,用于进行结果分发。请求队列中的网络请求会首先被放入Cache任务队列中,被CacheDispatcher线程调度。CacheDispatcher会试图从cache中取出该任务所请求的数据,如果命中则交给ResponseDelivery解析该数据并返回给应用程序;如果未命中,或者缓存失效等情况下,则将该请求任务加入到网络任务的队列中,供NetworkDispatcher进程调度。NetworkDispatcher请求结束则将结果交给ResponseDelivery作后续的处理。

3、从上面的分析中我们可以看出,我们所请求的数据主要存在两个个地方:Cache和网络。volley中分别使用Cache和HttpStack这两个接口来描述它们以及所需执行的操作。其中,HttpStack负责处理http网络请求,volley中有两种方式实现了HttpStack接口:基于 HttpURLConnection 的HurlStack 和 基于HttpClient 的HttpClientStack 。而Cache既可是基于SD卡,又可以基于内存。

4、通过上面所述的两个接口可以获取并操作我们请求的数据,这些数据主要分布在网络服务器和本地内存或SD卡中。

volley维护了一个请求队列来管理应用程序的网络请求,并采用了单例模式来保证一个应用程序只含有一个请求队列。通常情况下,我们会继承Application类,并通过newRequestQueue方法来创建一个请求队列。从源码中我们可以看出,在Android2.3以上使用了基于HttpUrlConnection的HurlStack处理网络请求,而2.3以下使用了基于httpclient的HttpClientStack来处理网络请求。这里的原因在上一篇文章中我们提到过,这里不再赘述。下面我们顺着请求的提交—>处理—>完成 的这条线路来研究一下框架的内部细节。

针对不同的网络请求,我们可以实现Request这个抽象类。该抽象类描述了请求的url,方式,head,body以及优先级等等信息。然而volley已经为我们实现了大部分的子类来满足各种需求。在Request的子类中,我们需要重写两个方法:

protected Response<T> parseNetworkResponse(NetworkResponse response) :用于将网络返回的字节流解析为合适的数据类型。

protected void deliverResponse(T response) :将解析好的数据传递给它的监听回调。

另外,如果我们自定义Request,通常也会重写getBody()方法来构建body内容;如果并未重写getBody方法,那么将会把getParams()方法返回的K-V值拼接起来的字节码作为body。

定义好了请求Request,接下来我们来通过源码来研究一下RequestQueue这个类。RequestQueue作为volley框架的核心类,负责管理应用程序的网络请求。我们在使用volley进行网络的时候,向请求队列提交了Request后发生了什么呢?请看下图。

RequestQueue使用一个set来存储一个未处理的请求。当我们提交一个请求后,RequestQueue会将该请求加入到这个集合中:

private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();

我们在上面已经提到过,我们主要从cache和网络来请求数据。于是,在RequestQueue中维护了两个请求队列:cache请求队列CacheQueue和网络请求队列NetworkQueue:

private final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<?>>();
private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?>>();

我们可以看到,这两个队列采用了优先阻塞队列PriorityBlockingQueue来维护请求。每个新请求都会先放入cache队列等待调度,只有在cache未命中或无效的情况下会被放入网络请求队列。但是,如果一个请求在处理的同时,又有相同url的请求怎么办呢?显然重复的请求只要到cache中去取就好了,无需再次进行网络请求,所以volley采用一个map来管理重复的请求,将它们暂时放入map中等待:

private final Map<String, Queue<Request<?>>> mWaitingRequests = new HashMap<String, Queue<Request<?>>>();

提交了请求之后,接下来的工作就是对请求进行处理了,接下来我们来研究一下volley的调度策略。

对应于两种请求队列,分别使用CacheDispatcher和NetworkDispatcher两个线程来调度分发,上面已经介绍过这两种线程了,来看一下RequestQueue初始化和启动的代码:

public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
// Create the cache dispatcher and start it.
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// Create network dispatchers (and corresponding threads) up to the pool size.
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}

从源码中我们可以看出RequestQueue启动了一个CacheDispatcher线程和多个NetworkDispatcher线程来完成调度分发,下面我们来研究一下这两种线程。

CacheDispatcher

CacheDispatcher启动后会不断地轮询mCacheQueue,队列为空时则等待;如果请求的cache未命中,过期或者无效,则会把该请求加入到mNetworkQueue中。当请求处理完成后则会将结果交给ResponseDelivery做最后的处理。具体流程如下:

NetworkDispatcher

NetworkDispatcher不断轮询mNetworkQueue取出请求去执行,队列为空则等待,请求处理结束则将结果传递给 ResponseDelivery 去执行后续处理,并判断结果是否要进行缓存。具体流程如下:

从图中可以看到,无论是CacheDispatcher还是NetworkDispatcher,请求结束后都会将结果交给ResponseDelivery这个接口来做后续处理,在ResponseDelivery中主要有三个方法还传递请求结果或者错误:

public void postResponse(Request<?> request, Response<?> response) //  用于传递请求结果, request 和 response 参数分别表示请求信息和返回结果信息。
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) //用于传递请求结果,并在完成传递后执行 Runnable
public void postError(Request<?> request, VolleyError error); //用于传输请求错误

当一个请求处理完成后,首先需要将当前处理集合mCurrentRequests 中的请求移除,然后在重复请求的mWaitingRequests中查询是否有正在等待的重复请求,如果有则放入缓存队列中处理。

总结

通过从请求的创建—>提交—>处理—>完成 这条主线,本文简单的介绍了volley框架的执行流程以及部分细节。volley的整个框架采用了大量的面向接口的设计,保证了使用的灵活性和框架的开放性。同时,volley又实现了很多接口来帮助开发者应对各种需求,在保证灵活性的同时也减少了很多繁琐的工作。

Android网络编程随想录(四)的更多相关文章

  1. Android网络编程随想录(1)

    本系列文章对整个Android网络编程进行了总结,包括基本的TCP/IP协议,HTTP协议,HTTPS协议,HttpClient,UrlConnection,一些网络通信的库到棉花糖新加入的OKHTT ...

  2. Android网络编程随想录(3)

    大多数Android的app都会使用HTTP协议来发送和接收数据.在Android开发中,通常使用两种http客户端:一个是Apache的HttpClient,另一个是HttpURLConnectio ...

  3. Android网络编程随想录(2)

    上篇文章介绍了传输层TCP协议的理论知识,本文主要介绍了TCP协议基础之上HTTP协议和HTTPS协议的理论知识. HTTP协议基于TCP协议定义了客户端向服务器请求数据的方式,它是面向事务的应用层协 ...

  4. Android网络编程(一)HTTP协议原理

    相关文章 Android网络编程(一)HTTP协议原理 Android网络编程(二)HttpClient与HttpURLConnection Android网络编程(三)Volley使用方法全解析 A ...

  5. Android网络编程(十)Retrofit2后篇[注解]

    G相关文章 Android网络编程(一)HTTP协议原理 Android网络编程(二)HttpClient与HttpURLConnection Android网络编程(三)Volley用法全解析 An ...

  6. Android网络编程系列 一 TCP/IP协议族

    在学习和使用Android网路编程时,我们接触的仅仅是上层协议和接口如Apache的httpclient或者Android自带的httpURlconnection等等.对于这些接口的底层实现我们也有必 ...

  7. Android网络编程系列 一 Socket抽象层

     在<Android网络编程>系列文章中,前面已经将Java的通信底层大致的描述了,在我们了解了TCP/IP通信族架构及其原理,接下来我们就开始来了解基于tcp/ip协议层的Socket抽 ...

  8. 【Android 应用开发】Android 网络编程 API笔记 - java.net 包 权限 地址 套接字 相关类 简介

    Android 网络编程相关的包 : 9 包, 20 接口, 103 类, 6 枚举, 14异常; -- Java包 : java.net 包 (6接口, 34类, 2枚举, 12异常); -- An ...

  9. Android 网络编程 API笔记 - java.net 包 权限 地址 套接字 相关类 简介

    Android 网络编程相关的包 : 9 包, 20 接口, 103 类, 6 枚举, 14异常; -- Java包 : java.net 包 (6接口, 34类, 2枚举, 12异常); -- An ...

随机推荐

  1. Vue2 封装的 Quill 富文本编辑器组件 Vue-Quill-Editor

    1.安装 npm install vue-quill-editor --save 2.使用 import { quillEditor } from 'vue-quill-editor' 3.组件中 & ...

  2. vue.js的ajax和jsonp请求

    首先要声明使用ajax 在 router下边的 Index.js中 import VueResource from 'vue-resource'; Vue.use(VueResource); ajax ...

  3. yum的方式搭建mysql

    1.安装相应的软件yum install mysql : 安装mysql客户端 yum install mysql-server 安装服务端 yum install mysql-devel 安装相关的 ...

  4. eas之根据bostype查找实体

    select * from  T_PF_BOSOBJECT;所有实体都有一个唯一的类型ID:BOSTYPEID,8位字符串,可切换到“源代码“页签,查找bostype标签里的值.这个ID的用途主要有: ...

  5. 10.3 .NET 3.5 中的扩展方法

    10.3.4 用 Select 方法和匿名类型进行投影 class Program { static void Main(string[] args) { , ).Where(x => x % ...

  6. <a>标签中的href伪协议 标签: html 2016-12-24 22:38 365人阅读 评论(0)

    <a id="jsPswEdit" class="set-item" href="javascript:;">修改密码</ ...

  7. python第八周:socket网络编程

    1.socket网络编程 1.1概念: 网络套接字是跨计算机网络的连接的端点.今天,计算机之间的大多数通信都基于互联网协议;因此大多数网络套接字都是Internet套接字.更准确地说,套接字是一个句柄 ...

  8. Unity中使用摇杆控制

    Unity中使用摇杆控制 本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/50 ...

  9. Linux中tty是什么(tty1~7)

    tty:终端设备的统称. tty一词源于Teletypes,或者teletypewriters,原来指的是电传打字机,是通过串行线用打印机键盘通过阅读和发送信息的东西,后来这东西被键盘与显示器取代,所 ...

  10. 《编程导论(Java)&#183;2.1.2 啊,我看到了多态》-什么是多态(polymorphism)

    1.不明觉厉 很多人学习多态时,会认为. 之所以不明觉厉,由于多态的定义:事物存在的多种表现形态:而后,有人将重载(overload).改写(override).多态变量和泛型归结于同一个术语&quo ...