去年 11 月开始写一个扩展 Volley 的网络通信框架 ———— VolleyEx,虽然 Github 上已经有 VolleyPlus 等优秀的扩展框架,但我想借此研究异步加载网络请求的原理,学习下 Volley 的代码设计,所以还是自己重新写一个扩展框架。并且把我一步一步扩展 Volley 的过程用博客的形式记录下来,希望可以帮助大家更好地理解 Volley 及网络加载。在扩展 Volley 之前,需要先了解其大致框架,明白它的优势与不足,这也是这篇文章的主要内容。
Volley 简介
Volley 是 2013 Google I/O Session 推出的一个网络通信框架,便于异步加载网络请求和图片请求,适用于数据量小和通信频繁的网络操作。但是对于数据量大的网络(例如文件下载),Volley 的表现就很糟糕了,这种情况推荐用 DownloadManager。
根据 Android 官方培训课程中 Volley 教程的说明,Volle 有下面几个优点:
自动调度网络请求
高并发网络连接
通过标准的 HTTP cache coherence 缓存磁盘,内存透明的响应
支持指定请求的优先级
框架容易定制,如重试和回退功能
清晰的加载流程使异步加载网络数据并正确地显示到UI更加简单
包含了调试与追踪工具
关于 Volley 的不足之处,在解析完其框架再来讨论。
Volley 的官方项目地址: https://android.googlesource.com/platform/frameworks/volley/
Volley 的 Github 地址(由国人做的映射): https://github.com/mcxiaoke/android-volley
这是 2013 Google I/O 大会上关于 Volley 的文档: Volley-Easy,Fast Networking for Android.pdf
Volley 工作流程
上面提到的 Volley 文档中就有一张工作流程图:
从上面的流程图可以看出,Request 添加到请求队列后,先是给缓存调度器处理。如果该请求有一个有效的缓存则直接解析缓存后传递到主线程,否则把它给网络调度器,网络调度器把这些请求按规则循环处理,当一个请求处理时会通过 HTTP 协议加载数据,然后解析数据并写入缓存,之后把解析后的 Response 传递到主线程。
接下来以 Volley 的流程来大致分析源代码。
启动
在添加请求到请求队列前,必须先启动 Volley 通信框架,在代码上来看就是Volley.newRequestQueue(context)
,这行代码主要是新建一个 RequestQueue 对象并调用其 start 方法,关键代码如下:
|
|
start
方法主要是启动一个缓存调度器和几个网络调度器,这两个分配器都是继承 Thread,其中默认网络调度器的数量是 4 个,所以默认是启动 1 个缓存调度器线程和 4 个网络调度器线程作为常驻线程。
添加请求到分发队列
接下来就是通过RequestQueue.add(request)
添加请求到分发队列。
|
|
在add
方法中,先将 Request 添加到当前请求队列,然后判断是否需要缓存,如果不需要缓存则直接添加到网络队列,默认shouldCache
方法返回 true,接下来根据cache key
判断是否有重复的请求,如果有则只是把请求添加到等待列表,如果没有的重复的请求则还会添加到缓存队列。所以从这里可以看出 Volley 对于重复的请求只会加载第一个请求,节省资源。
缓存调度器处理请求
CacheDispatcher
缓存调度器线程会一直取出缓存队列的请求处理:
|
|
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
这行说明缓存线程是在后台线程中运行,后面在while(true)
循环中从缓存队列中拿请求出来处理,下面重点看缓存的处理逻辑,Cache.Entry entry = mCache.get(request.getCacheKey());
先获得缓存数据,如果没有缓存或缓存是已经过期则添加请求到网络队列。然后解析缓存数据,如果缓存不需要刷新就直接传递解析后的 response 到主线程,如果需要刷新则把 response 传递到主线程后还会把请求添加网络队列。
上面代码中mDelivery.postResponse()
其实是用来传递 response 的,可以看下面的代码:
|
|
ExecutorDeliver 作为传递器,负责传递解析后的 Response 或者 error,是运行在主线程中的,主要是通过Handler.post(Runnable runnable)
方法传递的。缓存调度器和网络调度器都是通过它来传递 Response 的,ResponseDeliveryRunnable
的 run 方法是这个类的重点。run 方法的第一个 If 语句可以保证取消请求后,之后解析后的数据不会传递到主线程。第二个 If 语句中mRequest.deliverResponse(mResponse.result);
的 deliverResponse 是 Request 类的抽象方法,需要子类实现,这句话就是把解析后的数据传递给主线程了。而第三个 If 语句中mResponse.intermediate
默认是为 false 的,在之前的CacheDispatcher
代码中可以看到当缓存需要刷新会设置为 true,所以当缓存调度器找到有效不需要刷新的缓存到这里时和网络调度器加载完数据解析后到这里时都会走到mRequest.finish("done");
,这里会做一些完成这个请求的一些动作。
网络调度器处理请求
如果没有找到未过期缓存或者缓存需要刷新时,请求就会到网络队列,还有一开始的shouldCache()
返回false的话也会到缓存队列,下面看网络调度器如何处理网络队列中的请求。
|
|
网络调度器线程也是运行在后台的,一直循环去网络队列中取请求处理,mNetwork.performRequest(request);
就是通过 HTTP 协议加载网络数据的,这里不深入研究,因为现在主要是搞清楚 Volley 的大致框架。接下来request.parseNetworkResponse(networkResponse);
是解析数据的,Volley 的解析过程都是在后台线程进行的,之后写入缓存,然后传递解析后的 Response 到主线程。
到这里已经跟着 Volley 的工作流程走了一遍,Volley 的大致框架也就出来了,现在再来看看那张流程图
我只是跟着 Volley 的工作流程大致分析了一下工作流程和大致框架,更深入的解析大家也可以看下 CodeKK 的 Volley 源码解析,当然后面的一步一步扩展 Volley 系列文章中也会更加深入地分析 Volley,同时明白从那些方面扩展 Volley。