Volley应该是比较久远的产物了。google在2013 IO发布,但也可以借鉴学习毕竟是google工程师的AOSP产物。
下面从范例代码分析Volley的结构和核心源码。

//创建RequestQueue 队列RequestQueue mQueue = Volley.newRequestQueue(context);//url        String url = "http//www.baidu.com";//返回结果处理        Response.Listener listener =  new Response.Listener<String>() {@Overridepublic void onResponse(String response) {                Log.e("TAG",response);            }        };//错误返回结果处理        Response.ErrorListener errorListener = new Response.ErrorListener() {@Overridepublic void onErrorResponse(VolleyError error) {                Log.e("TAG",error.getMessage(),error);            }        };//Request请求StringRequest stringRequest = new StringRequest(Request.Method.GET,url,               listener, errorListener);//将请求加入RequestQueue 队列mQueue.add(stringRequest);复制代码

实际就三个步骤:

  • 创建队列 RequestQueue
  • 创建Request请求
  • 将Request请求添加到队列RequestQueue

创建RequestQueue

RequestQueue mQueue = Volley.newRequestQueue(context);

调用两个参数的构造方法:

这个方法有点长截取重要部分
主要是对于9以上版本是创建BasicNetwork对象
然后调用newRequestQueue(context, network);
看看BasicNetwork对象干什么的?

创建了一个4*1024大小的二进制数组和将new HurlStack()传进来。
好,看到这里先把这个类放着,后边再继续看到底做了些什么。

将context和BasicNetwork传进来

newRequestQueue(context, network);

第一句代码就不说了,就是在设备上创建一个文件放缓存;

主要看后面两句:

RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();

这里看见创建了一个ExecutorDelivery,和我们十分熟悉的Handler,而这个Handler是主线程的Handler;看看这个ExecutorDelivery是干什么的:

看到这里,ExecutorDelivery主要就是创建一个主线程的Handler,将返回的信息给主线程的Handler处理。Runnable 就是给主线程处理的。
继续看刚刚的构造方法

public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}

  • cache 就是DiskBasedCache主要就是缓存
  • network 就是BasicNetwork主要就是发送http请求的,但真正实现发送是HurlStack,后面会带大家看源码讲解。
  • mDispatchers就是创建一个NetworkDispatcher数组
  • mDelivery 刚才已经看了代码就是给主线程Handler处理回调结果用的

RequestQueue创建完毕下一步

queue.start()

stop();英文注解写得很清楚就是确保目前正在执行的dispatcher停止(不管是网络的还是缓存的)

之后的代码逐行解释

mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);

  • mCacheQueue是缓存队列
  • mNetworkQueue是网络请求队列
  • mCache刚才说了是DiskBasedCache主要就是缓存
  • mDelivery刚才已经看了代码就是给主线程Handler处理回调结果用的

这里多了个WaitingRequestManager,看看干什么的:

先放着,构造函数并没有干什么

mCacheDispatcher.start();
我们看看CacheDispatcher类

继承Tread,那么start()即是调用run()

@Overridepublic void run() {if (DEBUG) VolleyLog.v("start new dispatcher");//最高优先级        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);// Make a blocking call to initialize the cache.        mCache.initialize();while (true) {try {// Get a request from the cache triage queue, blocking until// at least one is available.final Request<?> request = mCacheQueue.take();                request.addMarker("cache-queue-take");// If the request has been canceled, don't bother dispatching it.if (request.isCanceled()) {                    request.finish("cache-discard-canceled");continue;                }// Attempt to retrieve this item from cache.                Cache.Entry entry = mCache.get(request.getCacheKey());if (entry == null) {                    request.addMarker("cache-miss");// Cache miss; send off to the network dispatcher.if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {                        mNetworkQueue.put(request);                    }continue;                }// If it is completely expired, just send it to the network.if (entry.isExpired()) {                    request.addMarker("cache-hit-expired");                    request.setCacheEntry(entry);if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {                        mNetworkQueue.put(request);                    }continue;                }// We have a cache hit; parse its data for delivery back to the request.                request.addMarker("cache-hit");                Response<?> response = request.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));                request.addMarker("cache-hit-parsed");if (!entry.refreshNeeded()) {// Completely unexpired cache hit. Just deliver the response.//发送请求                    mDelivery.postResponse(request, response);                } else {// Soft-expired cache hit. We can deliver the cached response,// but we need to also send the request to the network for// refreshing.                    request.addMarker("cache-hit-refresh-needed");                    request.setCacheEntry(entry);// Mark the response as intermediate.                    response.intermediate = true;if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {// Post the intermediate response back to the user and have// the delivery then forward the request along to the network.                        mDelivery.postResponse(request, response, new Runnable() {@Overridepublic void run() {try {                                    mNetworkQueue.put(request);                                } catch (InterruptedException e) {// Restore the interrupted status                                    Thread.currentThread().interrupt();                                }                            }                        });                    } else {// request has been added to list of waiting requests// to receive the network response from the first request once it returns.                        mDelivery.postResponse(request, response);                    }                }            } catch (InterruptedException e) {// We may have been interrupted because it was time to quit.if (mQuit) {return;                }            }        }    }复制代码

方法比较长,我说重点:
首先设置了该线程为最高级别,然后缓存初始化,之后就是一个无限循环。里面首先在缓存队列拿出一个request,如果该request是已经取消就退出循环。不是继续往下走,从缓存mCache取一个entry,如果是空的话,mWaitingRequestManager.maybeAddToWaitingRequests(request),调用这句。看看干什么的:

private synchronized boolean maybeAddToWaitingRequests(Request<?> request) {            String cacheKey = request.getCacheKey();// Insert request into stage if there's already a request with the same cache key// in flight.if (mWaitingRequests.containsKey(cacheKey)) {// There is already a request in flight. Queue up.                List<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);if (stagedRequests == null) {                    stagedRequests = new ArrayList<Request<?>>();                }                request.addMarker("waiting-for-response");                stagedRequests.add(request);                mWaitingRequests.put(cacheKey, stagedRequests);if (VolleyLog.DEBUG) {                    VolleyLog.d("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);                }return true;            } else {// Insert 'null' queue for this cacheKey, indicating there is now a request in// flight.                mWaitingRequests.put(cacheKey, null);                request.setNetworkRequestCompleteListener(this);if (VolleyLog.DEBUG) {                    VolleyLog.d("new request, sending to network %s", cacheKey);                }return false;            }        }复制代码

由于我们是第一次作请求,基本就没有缓存,所以是走else部分:

里面只将cacheKey给mWaitingRequests存了起来,然后就是我截图红色框部分,最后返回false;
setNetworkRequestCompleteListener实现接口,其实就是这个:

继续看回之前的:

因为返回false,即调用mNetworkQueue.put(request);
网络队列添加了一个request,然后跳出循环;

继续看回RequestQueue的start():

刚才走到红色箭头,继续往下走:
由于mDispathcers的容量设置了4个,所以循环4次:
里面就是new一个NetworkDispatcher

  • mNetworkQueue是网络队列
  • network 就是BasicNetwork主要就是发送http请求的,但真正实现发送是HurlStack
  • cache 就是DiskBasedCache主要就是缓存
  • mDelivery 刚才已经看了代码就是给主线程Handler处理回调结果用的

networkDispatcher.start();
看看NetworkDispatcher类:

和CacheDispatcher类一样,就不多说,直接看run()

 @Overridepublic void run() {        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);while (true) {//获取系统开机直到现在的时间包含睡眠时间long startTimeMs = SystemClock.elapsedRealtime();            Request<?> request;try {// Take a request from the queue.                request = mQueue.take();            } catch (InterruptedException e) {// We may have been interrupted because it was time to quit.if (mQuit) {return;                }continue;            }try {                request.addMarker("network-queue-take");// If the request was cancelled already, do not perform the// network request.if (request.isCanceled()) {                    request.finish("network-discard-cancelled");                    request.notifyListenerResponseNotUsable();continue;                }                addTrafficStatsTag(request);// Perform the network request.//执行http请求mNetwork 是BasicNetwork                NetworkResponse networkResponse = mNetwork.performRequest(request);                request.addMarker("network-http-complete");// If the server returned 304 AND we delivered a response already,// we're done -- don't deliver a second identical response.if (networkResponse.notModified && request.hasHadResponseDelivered()) {                    request.finish("not-modified");                    request.notifyListenerResponseNotUsable();continue;                }// Parse the response here on the worker thread.                Response<?> response = request.parseNetworkResponse(networkResponse);                request.addMarker("network-parse-complete");// Write to cache if applicable.// TODO: Only update cache metadata instead of entire record for 304s.if (request.shouldCache() && response.cacheEntry != null) {                    mCache.put(request.getCacheKey(), response.cacheEntry);                    request.addMarker("network-cache-written");                }// Post the response back.                request.markDelivered();                mDelivery.postResponse(request, response);                request.notifyListenerResponseReceived(response);            } catch (VolleyError volleyError) {                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);                parseAndDeliverNetworkError(request, volleyError);                request.notifyListenerResponseNotUsable();            } catch (Exception e) {                VolleyLog.e(e, "Unhandled exception %s", e.toString());                VolleyError volleyError = new VolleyError(e);                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);                mDelivery.postError(request, volleyError);                request.notifyListenerResponseNotUsable();            }        }    }复制代码

NetworkDispatcher这个类和CacheDispatcher很相似,不同之处只是CacheDispatcher是通过缓存获得返回结果,而NetworkDispatcher是通过发送网络请求获得。

Process.setThreadPriority(Process.THREADPRIORITYBACKGROUND);

第一步也是设置该线程为最高级别;然后就是一个无限循环;

request = mQueue.take();

就是从网络集合获取request对象;

这句代码与CacheDispatcher不同,看到注解啦吧;

这里mNetwork是什么呢?其实就是

BasicNetwork,看看它的performRequest()方法:

/**     * 执行Request请求     * */    @Overridepublic NetworkResponse performRequest(Request<?> request) throws VolleyError {//返回一个开机以来包括睡眠的度过时间long requestStart = SystemClock.elapsedRealtime();while (true) {            HttpResponse httpResponse = null;byte[] responseContents = null;            List<Header> responseHeaders = Collections.emptyList();try {// Gather headers.                Map<String, String> additionalRequestHeaders =                        getCacheHeaders(request.getCacheEntry());//返回http结果                httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);//状态码int statusCode = httpResponse.getStatusCode();//response头部                responseHeaders = httpResponse.getHeaders();// Handle cache validation./**                 * 当出现304(服务端有缓存且有效)会自己构建一个Response返回                 * */if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {/**                     * 304:                     * Not Modified 客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档                     * 返回 304 的时候已经做了一次数据库查询,但是可以避免接下来更多的数据库查询,                     * 并且没有返回页面内容而只是一个 HTTP Header,                     * 从而大大的降低带宽的消耗,对于用户的感觉也是提高。                     * 服务器会自动完成 Last Modified(缓存文件的) 和 If Modified Since(请求中包含的)                     * */                    Entry entry = request.getCacheEntry();if (entry == null) {return new NetworkResponse(HttpURLConnection.HTTP_NOT_MODIFIED, null, true,                                SystemClock.elapsedRealtime() - requestStart, responseHeaders);                    }// Combine cached and response headers so the response will be complete.//entry为Response的body                    List<Header> combinedHeaders = combineHeaders(responseHeaders, entry);return new NetworkResponse(HttpURLConnection.HTTP_NOT_MODIFIED, entry.data,true, SystemClock.elapsedRealtime() - requestStart, combinedHeaders);                }// Some responses such as 204s do not have content.  We must check.                InputStream inputStream = httpResponse.getContent();if (inputStream != null) {                  responseContents =                          inputStreamToBytes(inputStream, httpResponse.getContentLength());                } else {// Add 0 byte response as a way of honestly representing a// no-content request.                  responseContents = new byte[0];                }// if the request is slow, log it.long requestLifetime = SystemClock.elapsedRealtime() - requestStart;//记录请求时间                logSlowRequests(requestLifetime, request, responseContents, statusCode);if (statusCode < 200 || statusCode > 299) {throw new IOException();                }return new NetworkResponse(statusCode, responseContents, false,                        SystemClock.elapsedRealtime() - requestStart, responseHeaders);            } catch (SocketTimeoutException e) {                attemptRetryOnException("socket", request, new TimeoutError());            } catch (MalformedURLException e) {throw new RuntimeException("Bad URL " + request.getUrl(), e);            } catch (IOException e) {int statusCode;if (httpResponse != null) {                    statusCode = httpResponse.getStatusCode();                } else {throw new NoConnectionError(e);                }                VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());                NetworkResponse networkResponse;if (responseContents != null) {                    networkResponse = new NetworkResponse(statusCode, responseContents, false,                            SystemClock.elapsedRealtime() - requestStart, responseHeaders);if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED ||                            statusCode == HttpURLConnection.HTTP_FORBIDDEN) {                        attemptRetryOnException("auth",                                request, new AuthFailureError(networkResponse));                    } else if (statusCode >= 400 && statusCode <= 499) {// Don't retry other client errors.throw new ClientError(networkResponse);                    } else if (statusCode >= 500 && statusCode <= 599) {if (request.shouldRetryServerErrors()) {                            attemptRetryOnException("server",                                    request, new ServerError(networkResponse));                        } else {throw new ServerError(networkResponse);                        }                    } else {// 3xx? No reason to retry.throw new ServerError(networkResponse);                    }                } else {                    attemptRetryOnException("network", request, new NetworkError());                }            }        }    }复制代码

很长一大段,我们看核心的:

一开始想通过request获取缓存entry通过entry获取http头部信息;
然后

httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);

mBaseHttpStack又是什么玩意呢?
其实就是之前

所以上边我已经透露过真正执行http请求的是HurlStack

这里我不过多截图了,有兴趣可以看看Volley源码HurlStack的executeRequest方法。其实这里它是通过HttpURLConnection来完成http请求的。相信大家对HttpURLConnection都很熟悉,在没有使用过任何网络请求框架的时候Android不是使用Apache的httpclient就是使用java里面的HttpURLConnection。

我们继续BasicNetwork的performRequest:

请求返回获取状态码,获取返回的头部信息
通过状态码判断如果是304的话:
说明客户端有缓冲的文档,文档信息还是有效的。

如果不是304状态码,

获取了返回的内容
最后返回NetworkResponse对象

继续看回NetworkDispatcher:
获取到返回的NetworkResponse

request.parseNetworkResponse(networkResponse);
这句代码只是将返回的内容通过头部编码信息转换一下;

其实这里还有一段代码就是将返回的信息作缓存,下次如果有同一个请求的时候如果状态码返回304就可以通过缓存获取不走网络请求内容了。

核心代码是

mDelivery.postResponse(request, response);
这段代码其实就是ExcutorDelivery的postResponse

下面截图是ResponseDeliveryRunnable的run方法

就是将返回结果给Request的deliverResponse或deliverError

这里只截了deliverResponse,看看里面其实就是我们范例代码的listener。

整个Volley的请求流程就是这样一个流程了。现在总结一下:

  • Volley实际是通过HttpURLConnection完成网络请求的;
  • Volley最大的特点是有缓存队列,先走缓存队列,缓存队列没有信息再走网络队列;
  • Volley没有创建线程池,而是默认创建4个Thread执行网络请求;
  • 最后Volley使用了PriorityBlockingQueue这个队列,该队列特点线程安全的可以根据优先级别获取元素,而存放在PriorityBlockingQueue里面的元素需要实现Comparable接口;

最后放一张自己画的VolleyUML图,宏观理解一下Volley的结构:

由于只是从范例代码的角度看源码,有些类没有涉及到看,如果有什么问题欢迎指正和学习,共同交流。

转载于:https://juejin.im/post/5a30aaea6fb9a0450f21ec67

2017-10-9(Volley使用范例源码分析)相关推荐

  1. 我的内核学习笔记10:Intel GPIO驱动源码分析

    本文对Intel e3800的GPIO驱动源码进行分析. 一.概述 1.1 内核配置 Intel e3800的GPIO在Linux内核中使用的驱动名为gpio_ich(为了行文方便,将对应的设备称为& ...

  2. Envoy 源码分析--network L4 filter manager

    目录 Envoy 源码分析--network L4 filter manager FilterManagerImpl addWriteFilter addReadFilter addFilter in ...

  3. Retrofit2.0 源码分析

    前言 注解式的框架非常火,注解以其轻量,简洁等特性被人们所喜爱者,关键是它解藕.网络请求的框架非常多,比较受欢迎的当属retrofit和okHttp了.连retrofit都是基于okHttp之上开发的 ...

  4. 2017最新鑫众游戏大厅源码架设和全套手机版运营级别源码下载

    一. 源码包编译安装部署web服务器 1.安装nginx必须的依赖包 [root@test01 ~]# yum -y install gcc openssl-devel pcre-devel zlib ...

  5. Django源码分析10:makemigrations命令概述

    django源码分析 本文环境python3.5.2,django1.10.x系列 django源码分析-makemigrations命令概述 Django项目中的数据库管理命令就是通过makemig ...

  6. Volley源码分析

    下面是Volley的基本用法: 看到上图的第一步,创建请求队列,我们跟进去看看具体情况: 接下去看上图标记A处: 看到上图的B处: 创建了请求队列对象,然后调用了start方法,进去看下start方法 ...

  7. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(10)-系统菜单栏[附源码]

    构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(10)-系统菜单栏[附源码] 原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后 ...

  8. Web 开发中很实用的10个效果【附源码下载】

    在工作中,我们可能会用到各种交互效果.而这些效果在平常翻看文章的时候碰到很多,但是一时半会又想不起来在哪,所以养成知识整理的习惯是很有必要的.这篇文章给大家推荐10个在 Web 开发中很有用的效果,记 ...

  9. 《深入理解Spark:核心思想与源码分析》——3.10节创建和启动ExecutorAllocationManager...

    本节书摘来自华章社区<深入理解Spark:核心思想与源码分析>一书中的第3章,第3.10节创建和启动ExecutorAllocationManager,作者耿嘉安,更多章节内容可以访问云栖 ...

最新文章

  1. 常用的Percona-Toolkit工具
  2. 39 JavaScript中的严格模式
  3. 使用tab键分割的文章能快速转换成表格。( )_word排版技巧:活用Enter键提高工作效率...
  4. 标准的JS里,eval和window.eval属于不同的语法!
  5. 山西农信社计算机知识,山西人事考试网 山西农信社考试计算机知识高频考点(二)...
  6. 计算机视觉摔倒检测,基于计算机视觉的室内跌倒检测
  7. 虚拟机ping不通百度等外部网络
  8. shell脚本发邮件内容html,[转]Shell脚本中发送html邮件的方法
  9. C++ 异常变量的生命周期
  10. mybatis枚举自动转换(通用转换处理器实现)
  11. each 数据获取attr_Python数据分析 — 基于RFM的精细化用户分层
  12. 元胞计算机系统,元胞自动机
  13. C#中==操作符存在的缺陷
  14. 宁夏计算机科学与技术产业发展新趋势,2021年CCF数据库发展战略研讨会在宁夏银川顺利召开...
  15. 网络编程+并发编程总结
  16. spark on yarn 完全分部署_听说你熟悉Flink-On-Yarn的部署模式?
  17. 单片机C语言12864绘画,单片机驱动lcd12864液晶绘图功能程序+仿真
  18. 毕业论文排版素材大学计算机基础,毕业论文排版素材大学计算机基础实验.pdf...
  19. 装逼神器--黑客帝国的实现效果(linux环境系统)
  20. 基于星环TDH数据仓库典型总和场景数据流转设计

热门文章

  1. Winforn中使用FastReport实现点击导出按钮PDF预览并弹出另存为对话框
  2. EasyUI中Tabs标签页的简单使用
  3. SpringBoot+jquery实现post提交表单并添加隐藏域属性完成编辑功能
  4. Cordova打包的Vue项目在IOS无法拉起支付宝和微信支付
  5. Eclipse(javaweb)刚换工作空间之后,应该做哪几件事
  6. 字符的用意_通达信某些字符的意义及用法
  7. 如何学习配置webpack(一)
  8. ActiveMQ消息传递的两种方式
  9. SQL ALTER TABLE 语句
  10. LVS DR模式 负载均衡服务搭建