2017-10-9(Volley使用范例源码分析)
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使用范例源码分析)相关推荐
- 我的内核学习笔记10:Intel GPIO驱动源码分析
本文对Intel e3800的GPIO驱动源码进行分析. 一.概述 1.1 内核配置 Intel e3800的GPIO在Linux内核中使用的驱动名为gpio_ich(为了行文方便,将对应的设备称为& ...
- Envoy 源码分析--network L4 filter manager
目录 Envoy 源码分析--network L4 filter manager FilterManagerImpl addWriteFilter addReadFilter addFilter in ...
- Retrofit2.0 源码分析
前言 注解式的框架非常火,注解以其轻量,简洁等特性被人们所喜爱者,关键是它解藕.网络请求的框架非常多,比较受欢迎的当属retrofit和okHttp了.连retrofit都是基于okHttp之上开发的 ...
- 2017最新鑫众游戏大厅源码架设和全套手机版运营级别源码下载
一. 源码包编译安装部署web服务器 1.安装nginx必须的依赖包 [root@test01 ~]# yum -y install gcc openssl-devel pcre-devel zlib ...
- Django源码分析10:makemigrations命令概述
django源码分析 本文环境python3.5.2,django1.10.x系列 django源码分析-makemigrations命令概述 Django项目中的数据库管理命令就是通过makemig ...
- Volley源码分析
下面是Volley的基本用法: 看到上图的第一步,创建请求队列,我们跟进去看看具体情况: 接下去看上图标记A处: 看到上图的B处: 创建了请求队列对象,然后调用了start方法,进去看下start方法 ...
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(10)-系统菜单栏[附源码]
构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(10)-系统菜单栏[附源码] 原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后 ...
- Web 开发中很实用的10个效果【附源码下载】
在工作中,我们可能会用到各种交互效果.而这些效果在平常翻看文章的时候碰到很多,但是一时半会又想不起来在哪,所以养成知识整理的习惯是很有必要的.这篇文章给大家推荐10个在 Web 开发中很有用的效果,记 ...
- 《深入理解Spark:核心思想与源码分析》——3.10节创建和启动ExecutorAllocationManager...
本节书摘来自华章社区<深入理解Spark:核心思想与源码分析>一书中的第3章,第3.10节创建和启动ExecutorAllocationManager,作者耿嘉安,更多章节内容可以访问云栖 ...
最新文章
- 常用的Percona-Toolkit工具
- 39 JavaScript中的严格模式
- 使用tab键分割的文章能快速转换成表格。( )_word排版技巧:活用Enter键提高工作效率...
- 标准的JS里,eval和window.eval属于不同的语法!
- 山西农信社计算机知识,山西人事考试网 山西农信社考试计算机知识高频考点(二)...
- 计算机视觉摔倒检测,基于计算机视觉的室内跌倒检测
- 虚拟机ping不通百度等外部网络
- shell脚本发邮件内容html,[转]Shell脚本中发送html邮件的方法
- C++ 异常变量的生命周期
- mybatis枚举自动转换(通用转换处理器实现)
- each 数据获取attr_Python数据分析 — 基于RFM的精细化用户分层
- 元胞计算机系统,元胞自动机
- C#中==操作符存在的缺陷
- 宁夏计算机科学与技术产业发展新趋势,2021年CCF数据库发展战略研讨会在宁夏银川顺利召开...
- 网络编程+并发编程总结
- spark on yarn 完全分部署_听说你熟悉Flink-On-Yarn的部署模式?
- 单片机C语言12864绘画,单片机驱动lcd12864液晶绘图功能程序+仿真
- 毕业论文排版素材大学计算机基础,毕业论文排版素材大学计算机基础实验.pdf...
- 装逼神器--黑客帝国的实现效果(linux环境系统)
- 基于星环TDH数据仓库典型总和场景数据流转设计