volle介绍02

Posted 力能扛鼎

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了volle介绍02相关的知识,希望对你有一定的参考价值。

----------------------------------------------------------------------------

转载: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,是不是只要这样就已经开启了线程去请求数据了呢。没有这么简单,我们还要:

 

RequestQueue mQueue = Volley.newRequestQueue(context); //创建请求队列  
mQueue.add(stringRequest); //将请求添加到请求队列  

  

上面的操作,将我们构造的请求,添加到一个新建的请求队列里面。到此为止,我们就成功地发出了请求,并且在请求结束以后,会回调我们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的具体实现。

以上是关于volle介绍02的主要内容,如果未能解决你的问题,请参考以下文章

Android Volley框架的使用

Volley框架的优缺点-->

什么移动框架适合需要频繁进行数据库通信的项目?

毕业设计:文献参考

读懂Volley,必须要理解的几个问题

本地缓存