Volley设计思想和流程分析

Posted 岳阳楼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Volley设计思想和流程分析相关的知识,希望对你有一定的参考价值。

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

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

如需转载,请注明原作:

以上是关于Volley设计思想和流程分析的主要内容,如果未能解决你的问题,请参考以下文章

最全面的c++中类的构造函数高级使用方法及禁忌

最全面的c++中类的构造函数高级使用方法及禁忌

源码解析Volley框架

源码解析Volley框架

基本项目开发流程

Volley -- 图片处理方式源码分析