Android 浅析 Volley (二) 原理
前言
Linus Benedict Torvalds : RTFSC – Read The Fucking Source Code
概括
本文通过对Volley源码进行分析来打通Volley的流程,并且知晓其原理,Volley的整体结构比较简单,但是细节有很多值得学习的地方。
Volley初始化
RequestQueue mQueue = Volley.newRequestQueue(this);
Volley的初始化就是简单的一行代码,但是里面做的事情比较复杂了。
Volley
这是Volley系统最主要的类之一,它用来初始化缓存目录和初始化下载响应队列。
Volley.newRequestQueue(...)
Creates a default instance of the worker pool and calls
RequestQueue.start()
on it.
HttpStack stack = new HurlStack();
Network network = new BasicNetwork(stack);
File cacheDir = new File(...);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
这个函数不难,首先创建一个HttpStack
对象和创建一个缓存文件,然后用创建的对象生成一个RequestQueue
响应队列,最后调用RequestQueue.start()
函数开始运行。
那么我们开始深入去看下,首先看下RequestQueue。
RequestQueue
A request dispatch queue with a thread pool of dispatchers.
Callingadd(Request)
will enqueue the given Request for dispatch, resolving from either cache or network on a worker thread, and then delivering a parsed response on the main thread.
RequestQueue.RequestQueue(...)
Creates the worker pool.
初始化这里有几个关键的点:
1、DEFAULT_NETWORK_THREAD_POOL_SIZE:访问网络的线程数,默认是4条线程。
2、new ExecutorDelivery(new Handler(Looper.getMainLooper())):新建一个用来推出网络响应和错误的类。
3、new NetworkDispatcher[threadPoolSize]:新建四条网络连接的线程。
//@param cache A Cache to use for persisting responses to disk
//@param network A Network interface for performing HTTP requests
//@param threadPoolSize Number of network dispatcher threads to create
//@param delivery A ResponseDelivery interface for posting responses and errors
RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery)
RequestQueue.Start()
Starts the dispatchers in this queue.
首先是创建一个CacheDispatcher
缓存发送者,它是一个缓存发送的线程,然后调用CacheDispatcher
的start()
函数。
然后循环创建NetworkDispatcher
对象,因为默认的线程数是4,所以会循环4次创建网络调度(和相应的线程)到池大小,接着再调用start()
函数。
简单来说当调用了Volley.newRequestQueue(context)之后,就会有五个线程一直在后台运行,不断等待网络请求的到来,其中CacheDispatcher是缓存线程,NetworkDispatcher是网络请求线程。
RequestQueue.Stop()
Stops the cache and network dispatchers.
将所有缓存和网络发送者停止。
总结
通过这几行简单的函数就创建了Volley的缓存和网络发送者队列,为以后的请求建立了机制。
Volley添加请求
Volley的添加请求也很简单。
StringRequest stringRequest = new StringRequest(...);
mQueue.add(stringRequest);
创建一个Request对象并且添加进Queue队列就行了。看起来简单,实际,好吧,很复杂。
RequestQueue
RequestQueue.add()
这里函数本身比较简单易懂,首先将Request标识为当前队列,然后添加到mCurrentRequests
当前请求队列里。接着判断是否可以缓存,如果不行则直接调用网络队列进行请求。最后插入请求到现阶段,如果已经有一个缓存键的请求在进行。
总结
添加的操作是比较简单,但难的是里面的具体过程,缓存请求和网络请求都是独立线程,在添加到队列之后就会不断的去获取然后对网络或者缓存进行处理。
Volley 类分析
工具类
网络类
HttpStack
Performs an HTTP request with the given parameters.
用于处理 Http 请求,返回请求结果的接口。
HurlStack
based on HttpURLConnection.
实现 HttpStack 接口,基于HttpURLConnection进行各种请求方式的请求封装。
BasicNetwork
A network performing Volley requests over an HttpStack.
一个基于HttpStack执行Volley请求的网络。
调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse。
主要实现了以下功能:
1、利用 HttpStack 执行网络请求。
2、如果 Request 中带有实体信息,如 Etag,Last-Modify 等,则进行缓存新鲜度的验证,并处理 304(Not Modify)响应。
3、如果发生超时,认证失败等错误,进行重试操作,直到成功、抛出异常(不满足重试策略等)结束。
Network
An interface for performing requests.
接口类,作为网络请求的主要接口,传入需要访问的执行信息,并且返回一个NetworkResponse
网络响应对象。
缓存类
Cache
An interface for a cache keyed by a String with a byte array as data.
接口,一个可以获取请求结果,存储请求结果的缓存。
Entry get(String key)
通过 key 获取请求的缓存实体
void put(String key, Entry entry)
存入一个请求的缓存实体
void remove(String key)
移除指定的缓存实体
void clear()
清空缓存
Entry内部类
byte[] data : 请求返回的数据(Body 实体)
String etag Http : 响应首部中用于缓存新鲜度验证的 ETag
long serverDate Http : 响应首部中的响应产生时间
long ttl : 缓存的过期时间
long softTtl : 缓存的新鲜时间
Map
boolean isExpired() : 判断缓存是否过期,过期缓存不能继续使用
boolean refreshNeeded() : 判断缓存是否新鲜,不新鲜的缓存需要发到服务端做新鲜度的检测
NoCache
什么缓存都没有。不做任何操作的缓存实现类,可将它作为构建RequestQueue的参数以实现一个不带缓存的请求队列。
DiskBasedCache
Cache implementation that caches files directly onto the hard disk in the specified directory. The default disk usage size is 5MB, but is configurable.
void initialize()
Initializes the DiskBasedCache by scanning for all files currently in the specified root directory. Creates the root directory if necessary.
初始化,扫描缓存目录得到所有缓存数据摘要信息放入内存。
Entry get(String key)
Returns the cache entry with the specified key if it exists, null otherwise.
从缓存中得到数据。先从缓存头中得到头部信息,然后读取缓存数据文件得到内容。
void put(String key, Entry entry)
Puts the entry with the specified key into the cache.
将数据文件内容保存到缓存。先检查缓存是否已满,已满则先删除缓存中部分数据,然后再新建缓存文件。
void pruneIfNeeded(int neededSpace)
Prunes the cache to fit the amount of bytes specified.
检查是否能再分配 neededSpace 字节的空间,如果不能则删除缓存中部分数据。
void clear()
Clears the cache. Deletes all cached files from disk.
清空缓存。
void remove(String key)
Removes the specified key from the cache if it exists.
删除缓存中某个元素。
CacheHeader
Handles holding onto the cache headers for an entry.
缓存文件头部信息在entry里。
ByteArrayPool
ByteArrayPool is a source and repository of
byte[]
objects.
byte[] 的回收池,用于 byte[] 的回收再利用,减少了内存的分配和回收。 主要通过一个元素长度从小到大排序的ArrayList作为 byte[] 的缓存,另有一个按使用时间先后排序的ArrayList属性用于缓存满时清理元素。
PoolingByteArrayOutputStream
A variation of
java.io.ByteArrayOutputStream
that uses a pool of byte[] buffers instead of always allocating them fresh, saving on heap churn.
Authenticator
身份认证接口,用于基本认证或者摘要认证。这个类是 Volley 用于和身份验证打通的接口,比如 OAuth,不过目前的使用不是特别广泛和 Volley 的内部结合也不是特别紧密。
AndroidAuthenticator
继承 Authenticator,基于 Android AccountManager 的认证交互实现类。
基础类
NetworkResponse
Data and headers returned from performRequest(Request).
作为Network
接口返回的值,Request
的parseNetworkResponse(…)
参数,是 Volley 中用于内部 Response 转换的核心类。
封装了网络请求响应的 StatusCode,Headers 和 Body 等。
成员变量
int statusCode
Http 响应状态码
byte[] data
Body 数据
Map
响应 Headers
boolean notModified
表示是否为 304 响应
long networkTimeMs
请求耗时
内部 Response 转换流程图
CacheDispatcher
Provides a thread for performing cache triage on a queue of requests.
CacheDispatcher类是一个线程类,里面有四个比较重要的变量:
1、BlockingQueue
来自缓存的队列。
2、BlockingQueue
将会访问网络的队列。
3、Cache mCache
缓存的处理类。
4、ResponseDelivery mDelivery
回调响应的类。
CacheDispatcher.run()
在线程被创建后马上就被执行了。里面首先会对缓存路径进行初始化工作。接着就进入一个无限的循环等待里。
首先会从缓存队列出取出请求,如果队列为空则会一直阻塞直到队列有数据,接着尝试从缓存当中取出响应结果,如何为空的话则把这条请求加入到网络请求队列中,如果不为空的话再判断该缓存是否已过期,如果已经过期了则同样把这条请求加入到网络请求队列中,否则就认为不需要重发网络请求,直接使用缓存中的数据即可。之后会调用Request的parseNetworkResponse()方法来对数据进行解析,最后就是将解析出来的数据进行回调了。
CacheDispatcher 流程图
NetworkDispatcher
Provides a thread for performing network dispatch from a queue of requests.
NetworkDispatcher类是一个线程类,里面有四个比较重要的变量:
1、BlockingQueue
2、Network mNetwork 网络进度请求。
3、Cache mCache缓存的处理类。
4、ResponseDelivery mDelivery回调响应的类。
NetworkDispatcher.run()
在线程被创建后马上就被执行了。首先进入一个无限的循环等待里。等待任务进来。
在接受到请求后会直接访问网络发送请求,请求处理结束则将结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。
NetworkDispatcher 流程图
ResponseDelivery
一个返回结果的分发接口。
有三个分发接口:
1、解析一个从网络或缓存的响应并分发。
postResponse(Request> request, Response> response);
2、解析一个从网络或缓存的响应并分发。提供的运行将会在分发后被执行。
postResponse(Request> request, Response> response, Runnable runnable);
3、推送一个收到的错误。
postError(Request> request, VolleyError error);
RetryPolicy
Retry policy for a request.
void retry(VolleyError error) throws VolleyError
确定是否重试,参数为这次异常的具体信息。在请求异常时此接口会被调用,可在此函数实现中抛出传入的异常表示停止重试。
DefaultRetryPolicy
Default retry policy for requests.
实现 RetryPolicy,Volley 默认的重试策略实现类。主要通过在 retry(…) 函数中判断重试次数是否达到上限确定是否继续重试。
其中mCurrentRetryCount变量表示已经重试次数。
mBackoffMultiplier表示每次重试之前的 timeout 该乘以的因子。
mCurrentTimeoutMs变量表示当前重试的 timeout 时间,会以mBackoffMultiplier作为因子累计前几次重试的 timeout。