----------------------------------------------------------------------------
转载:http://blog.csdn.net/crazy__chen/article/details/46483329
----------------------------------------------------------------------------
Volley是一款由Google 推出的 Android 异步网络请求框架和图片加载框架,特别适合数据量小,通信频繁的网络操作。
大家可以在这个地址https://android.googlesource.com/platform/frameworks/volley/下载源码
Volley 的主要特点
(1). 扩展性强。Volley 中大多是基于接口的设计,可配置性强。
(2). 一定程度符合 Http 规范,包括返回 ResponseCode(2xx、3xx、4xx、5xx)的处理,请求头的处理,缓存机制的支持等。并支持重试及优先级定义。
(3). 默认 Android2.3 及以上基于 HttpURLConnection,2.3 以下基于 HttpClient 实现
(4). 提供简便的图片加载工具。
本专栏主要通过解析volley的源码,对它的整个架构进行分析,指出它这样设计架构,对应扩展性,耦合等优势。同时,指出volley对于网络请求,考虑到的细节方面,用于指导我们日后自己写网络框架的需求。另外还会根据volley的设计,说明它所应用的场景,volley所面临的不足,以及它本身可以怎么被更完善的扩展。
本篇文章作为专栏的开篇,会先给简单大家介绍volley的使用和volley的整个架构设计,通过对架构的介绍,希望大家对volley的实现模式有个大体的印象。在这篇文章中,大家不必要关注实现的细节(因为在接下来的文章会详细介绍),主要做到的是体会volley的设计思路,关注整体的实现。
网上介绍volley如何使用的文章有很多,大家可以自行搜索一下,这里我举一个简单的例子说明如何使用volley进行网络请求。
StringRequest stringRequest = new StringRequest("http://www.baidu.com",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
//处理响应
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
//处理错误
}
});
有上面的代码我们可以看出,我们实例化了一个StringRequest对象,其中有一个网络地址www.baidu.com,两个监听器
观察监听器里面的方法,一个参数是String response,一个是VolleyError error
从字面上的意思理解,我们创建了一个网络请求对象request,传入请求的地址,请求成功以后,会回调两个监听器的方法,两个监听器分别对应请求成功与请求失败的情况。
OK,是不是只要这样就已经开启了线程去请求数据了呢。没有这么简单,我们还要:
上面的操作,将我们构造的请求,添加到一个新建的请求队列里面。到此为止,我们就成功地发出了请求,并且在请求结束以后,会回调我们request里面的方法。
这就是volley最简单的使用。
有人会问,为什么要将请求加入队列里面,我直接new一个Request,然后在Request内部建立线程,请求数据,最后回调接口不就好了吗?
这样想是没有错,但是volley这样设计必然有自己的原因,就耦合过紧这一点而言,就可以知道我们上面提出的策略并不是那么的优雅(不代表不可行)。
那么volley是怎么做到呢?我们先来看一张volley的整体架构图(该图出自http://codekk.com/open-source-project-analysis/detail/Android/grumoon/Volley%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90,如果不允许转载,请联系我)
上面的图有点复杂,大家先不要纠结,我们先来看我们之前的结论,volley的使用过程是
1,创建request
2,将request放入队列RequestQueue
3,处理回调结果
注意到例子中使用的StringRequest,对应上面的图,找到Request和StringRequest可以知道,StringRequest是Request的一个子类,从字面上可以知道,这是一个字符串请求,另外还有json,image请求等,都是request的子类,所以我们设想,是不是不同的请求,有一个不同的类呢?
正是这样的,从面向对象的角度,我们把请求看成是一个类,所以我们每次的网络请求,只有new出一个类,然后加入队列就可以了。
那么队列里面是怎么样替我请求数据的呢?这里我给大家介绍其中的奥秘,大家只要了解过程就可以,不必深究细节。
其实RequestQueue里面有两个队列,一个我称为缓存队列mCacheQueue,一个称为网络队列mNetworkQueue
如果请求要求加入缓存队列(例如我们给request设置一个属性ShouldCache,然后提供set方法来设置),将会试图从硬盘缓存中获取数据,如果没有缓存,这个请求将被放入网络队列
如果请求不要求缓存,则直接加入网络队列。
加入队列以后,我们开启线程,从队列中取出请求。
可想而知,我们最好有一个线程CacheDispatcher从缓存队列中取,一个NetworkDispatcher从网络队列中取,然而网络请求往往大量,所以volley实际上有多个线程同时从网络队列中取出请求(这里涉及线程同步,volley使用PriorityBlockingQueue解决)
为什么要先建立几个线程,从队列中取,而不是每个request开启一个线程呢?这样做的好处是避免重复大量创建线程所带来的开销,另外由于所有的request都存在在一个RequestQueue里面,便于我们对request的管理,例如我们要关闭某个request,又或者我们请求了很多相同的request,对应这些操作,我们如果将request分散,是很难统一解决的,所以这样用类似线程池的思想,统一管理线程。
同时,这样做又会带来不利,因为实际请求线程的线程数目是固定的,意味着当request数目大于线程数目时,有的线程将被阻塞,造成效率下降,更多的问题,会在接下来的文章提到。
至于CacheDispatcher和NetworkDispatcher是怎么请求数据的呢?
对于NetworkDispatcher而言,必然是开启网络连接,然后获取数据的(例如url.openConnection),这是我们的常用实现,先不做详细解释(volley对这些实现进行了更详细的封装)
再来考虑,获得结果以后,我们怎么回调。
还是面向对象的思路,volley将响应结果封装成一个repsonse类(和request对应)
对应NetworkDispatcher而言,在它的run()方法里面,取得request以后,根据url请求数据,将数据封装成respsonse对象,再有一个分发器ResponseDelivery分发到对应的request
有人会问?解析到response以后,我们给request设计一个方法(例如将parseRespose(Respsonse respsonse))用于使用response,同时在这个方法内,回调监听器不就好了吗?为什么要多此一举,创建一个分发器呢?
原因是这样更灵活,但是还有一个重要的原因是,注意到我们回调,往往是在主线程中进行的(因为很可能要操作UI),如果我们在NetworkDispatcher(子线程)里面,直接回调,可能造成错误,这是ResponseDelivery存在的另外一个原因。
根据上面的结论,最后来看一张简单的流程图
根据流程分析,我们可以体会到,volley设计框架的基本思路,对比于我们简单的实现,volley的实现方式耦合更加松散,使用面向接口编程,同时使用更多组合方式而不是继承。使用了代理等设计模式,同时提高了线程的利用率。总之volley的架构设计又各种各样的好处。
我在这里介绍几个volley的功能,以及它考虑到的,而我们很可能没有考虑到的问题。这些问题或者说功能上的优势,会伴随着本专栏的深入让大家逐渐体会。
下面只是笼统的介绍,大家可以对比自己的想法,看看自己是不是有什么考虑不周的(如果是你实现这样一个框架的话)
1,Request的设计,我们在得到response之后,我们可能根据项目需求希望有不同形式的数据(例如string,bitmap,jsonObject),volley使用抽象编程,让我们可以继承Request实现自己对response的解析方式(意味着处理volley为我们提供的StringRequest类等,我们自定义request)
2,重试策略,网络请求可能因为网络原因失败(例如手机断网了),volley为我们提供了重试策略
3,终止请求,例如请求数据途中,我们希望终止该请求
4,request在队列中优先级的问题,例如我们有的请求比较紧迫,就应该排在队列的前面
5,重复请求的问题,利用有多个相同的request在队列之中,请求到其中一个,其他的可能就不必请求了,直接从缓存中取
6,异常的处理,例如io,403,402等授权错
7,地址重定向处理
8,网络问题的处理,例如我们断网了,volley使用了network这个类来处理这一类问题
9,使用HttpClient还是url.openConnection()去请求数据呢?
10,缓存的读取和存储,我们请求到网络数据以后,可以存入本地缓存
11,如何设置请求日志来记录请求信息,用于调试?
12,对于缓存写入和读取的效率优化
13,图片请求的处理
现在就让我们来小试牛刀,先来看Volley这个类,Volley作为整个框架的入口,其实就是创建了一个RequestQueue队列
public class Volley {
/**
* Default on-disk cache directory.
* 默认缓存目录名
*/
private static final String DEFAULT_CACHE_DIR = "volley";
/**
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
* You may set a maximum size of the disk cache in bytes.
* 创建一个默认工作池,并且启动请求队列
* @param context A {@link Context} to use for creating the cache dir.用于创建缓存目录
* @param stack An {@link HttpStack} to use for the network, or null for default.
* @param maxDiskCacheBytes the maximum size of the disk cache, in bytes. Use -1 for default size.
* 硬盘缓存最大值,-1则默认
* @return A started {@link RequestQueue} instance.
*/
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) {//如果没有限定stack
if (Build.VERSION.SDK_INT >= 9) {//adk版本在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)//小于等于-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;
}
可以看到,Volley的newRequestQueue()方法里面,根据版本创建了stack(分别是HurlStack和HttpClientStack)。至于不同adk版本会创建不同的stack,是由于android的版本原因,在9以上版本使用HurlStack比HttpClientStack更加好。
然后创建了网络类BasicNetwork,缓存类DiskBasedCache,然后使用这两个来创建RequestQueue对象。
或许现在大家还不明白这两个类的作用,大家只需要记得创建RequestQueue需要这两个类就可以了。
OK,作为本专栏的开篇文章,主要是为大家提供阅读的兴趣和思路。在接下来的文章里面,我就会结合具体的源代码,来说明volley的具体实现。