前面三篇文章从最基础的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. Table边框使用总结 ,只显示你要显示的边框

    表格的常用属性 基本属性有:width(宽度).height(高度).border(边框值).cellspacing(表格的内宽,即表格与tr之间的间隔). cellpadding(表格内元素的间隔, ...

  2. c#日期计算

    /// <summary> /// 计算日期的间隔(静态类) /// </summary> public static class dateTimeDiff { /// < ...

  3. 【转】Linux rpm 命令参数使用详解[介绍和应用]

    RPM是RedHat Package Manager(RedHat软件包管理工具)类似Windows里面的“添加/删除程序” rpm 执行安装包二进制包(Binary)以及源代码包(Source)两种 ...

  4. windows控制台(console)乱码

    在cmd中输入 CHCP 65001,实操有效.记录一下 永久设置,代码如下: Windows Registry Editor Version 5.00 [HKEY_CURRENT_USER\Cons ...

  5. android的数据与访问(2)-delphi xe7如何存取我的app配置参数文件?

    这种方法不推荐,因为该SharedPreference是android的方法.你想跨平台,在ios上就不能使用.建议还是用ini or xml.android因为读写该二种文件比较繁琐,所以推出自己简 ...

  6. Appendix B: Netsh Command Syntax for the Netsh Firewall Context

    11 out of 19 rated this helpful - Rate this topic Published: December 17, 2004 The following Netsh c ...

  7. magento 在全站或者某页面增加外联js或者css的方法

    1,在整站末尾增加外联资源 找到当前主题的布局文件cms.xml,在<default></default>添加如下代码: <reference name="be ...

  8. 如何用arcgis进行WGS84的投影坐标变换

    转自原文 如何用arcgis进行WGS84的投影坐标变换 通常情况下,要求WGS 84下的投影坐标,选择UTM投影. 1.UTM投影 UTM投影全称为“通用横轴墨卡托投影”UNIVERSAL TRAN ...

  9. 从编程的角度理解gradle脚本﹘﹘Android Studio脚本构建和编程[魅族Degao]

    本篇文章由嵌入式企鹅圈原创团队.魅族资深project师degao撰写. 随着Android 开发环境从Eclipse转向Android Studio,我们每一个人都開始或多或少要接触gradle脚本 ...

  10. 用Thinphp发送电子邮件的方法

    好长时间没有动php了,突然想用thinkphp发送电子邮件,可是查阅了书籍都写的非常乱.没有继续看下去.这里找到了一个比較好的方法: 第一步:首先我们要引入一个外部类库:Mail.class.php ...