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

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

如需转载,请注明原作:

?

时间: 2024-10-24 20:07:28

Volley设计思想和流程分析的相关文章

揭秘jbpm流程引擎内核设计思想及构架

作者 胡长城(银狐999) 1     前言 2     阅读本篇的基础准备 2.1      概念的基础 2.2      环境的基础 3     什么是流程引擎内核? 4     引擎内核所关注的四个主要问题 4.1      模型与定义对象 4.2      调度机制与算法 4.3      执行机制与状态 4.4      实例对象与执行环境 5    jbpm,“精简”的开源流程引擎 6    jBpm流程模型与定义对象 6.1      首先解决如何形式化描述一个流程的问题 6.2 

C++STL内存配置的设计思想与关键源码分析

说明:我认为要读懂STL中allocator部分的源码,并汲取它的思想,至少以下几点知识你要了解:operator new和operator delete.handler函数以及一点模板知识.否则,下面你很可能看不大明白,补充点知识再学习STL源码比较好. 下面会结合关键源码分析C++STL(SGI版本)的内存配置器设计思想.关键词既然是“思想”,所以重点也就呼之欲出了. 1.allocator的简短介绍 我阅读的源码是SGI公司的版本,也是看起来最清楚的版本,各种命名最容易让人看懂.alloc

02-FPGA设计流程介绍——小梅哥FPGA设计思想与验证方法视频教程配套文档

芯航线——普利斯队长精心奉献 课程目标: 1.了解并学会FPGA开发设计的整体流程 2.设计一个二选一选择器并进行功能仿真.时序仿真以及板级验证 实验平台:芯航线FPGA开发板.杜邦线 实验内容: 良好的文件夹设置以及工程管理是一个好的FPGA设计的基础,在学习之初就建立俩良好的习惯,会少走一些弯路.因此我们首先在新建的工程文件夹下面,分别建立如图2-1所示的子文件夹. 图2-1 FPGA工程子文件夹 上图中,prj为工程文件存放目录:rtl为verilog可综合代码存放目录:testbench

Nginx源码分析—架构设计思想

Nginx源码分析-架构设计思想 我任务nginx的源码可以分为三个部分,一个是在ngx_init_cycle之前,这个也算是为了重新启动nginx而准备的代码,比如说在这个时候可以接受外部的信号,也可以保存传递的参数,等等,当然在以后的函数中也考虑了是否正在重启nginx. 至于ngx_init_cycle这个函数,是一个很庞大的函数,在这个函数中可以看到调用了各个模块的钩子函数,这里又设计到了nginx结构体的使用,比如所有的模块都是ngx_module_t这个结构体,但是这个结构体中的ct

20 测试用例设计 流程分析

流程分析(别名:场景设计) 3 个流程: 基本流(正常操作,最后能够达到目标的流程:ATM正常输入密码,登录成功) 备选流(因错误操作.异常输入,导致流程存在反复,但最终能够达到目标:ATM 第一次输入错误密码,第二次输入正确密码,登录成功) 异常流(因错误操作.异常输入,导致业务没有正确完成:第二.三次都输入错误密码,导致吞卡) 使用方法 理解需求,确定业务流程:基本流.备选流.异常流 绘制流程图:再次确认流程路径 根据流程图,抽取测试路径:每次路径需包含一个从未走过的路径 细化路径,设计测试

Android 7.0 ActivityManagerService(8) 进程管理相关流程分析(2)

前一篇博客进程管理相关流程分析(1)里, 我们介绍了AMS中updateLruProcessLocked函数相关的流程. updateLruProcessLocked只是按照进程中运行的组件,粗略地定义了不同进程的优先级. 实际上,Android根据进程的oom_adj进行了更加细致的进程分类, 而AMS中的updateOomAdjLocked函数,就是用于更新进程的oom_adj值. 本篇博客中,我们来看看AMS中updateOomAdjLocked相关的流程. 一.ProcessList.j

JAVAWEB开发之Struts2详解(一)——Struts2框架介绍与快速入门、流程分析与工具配置以及Struts2的配置以及Action和Result的详细使用

Struts2框架介绍 三大框架:是企业主流JavaEE开发的一套架构.Struts2 + Spring + Hibernate 什么是框架?为什么要学习框架? 框架是实现部分功能的代码(半成品),使用框架简化企业级软件开发. Struts2与MVC? Struts是一款优秀的MVC框架 MVC:是一种思想,是一种模式,将软件分为Model模型.View视图.Controller控制器 JAVAEE软件三层架构:web层(表现层).业务逻辑层.数据持久层(Sun提供javaEE开发规范) Jav

《深入理解Android内核设计思想》

<深入理解Android内核设计思想> 基本信息 作者: 林学森 出版社:人民邮电出版社 ISBN:9787115348418 上架时间:2014-4-25 出版日期:2014 年5月 开本:16开 页码:687 版次:1-1 所属分类:计算机 > 软件与程序设计 > 移动开发 > Android 更多关于>>><深入理解Android内核设计思想> 编辑推荐 基于Android SDK最新版本 全面细致地剖析了进程/线程模型.内存管理.Bind

《深入理解Android内核设计思想》书本目录,及部分章节内容分享

第1篇 android编译篇 第1章 android系统简介 2  1.1 android系统发展历程 2  1.2 android系统特点 4  1.3 android系统框架 8 第2章 android源码下载及编译 10  2.1 android源码下载指南 10  2.1.1 基于repo和git的版本管理 10  2.1.2 android源码下载流程 11  2.2 原生态系统编译指南 12    2.2.1 建立编译环境 13    2.2.2 编译流程 15  2.3 定制产品的