Android 浅析 Volley (二) 原理

Android 浅析 Volley (二) 原理


前言

Linus Benedict Torvalds : RTFSC – Read The Fucking Source Code

概括

本文通过对Volley源码进行分析来打通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.
Calling add(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缓存发送者,它是一个缓存发送的线程,然后调用CacheDispatcherstart()函数。
然后循环创建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 类分析

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<String, String> responseHeaders : 响应的 Headers
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接口返回的值,RequestparseNetworkResponse(…)参数,是 Volley 中用于内部 Response 转换的核心类。

封装了网络请求响应的 StatusCode,Headers 和 Body 等。

成员变量

int statusCode Http 响应状态码
byte[] data Body 数据
Map<String, String> headers 响应 Headers
boolean notModified 表示是否为 304 响应
long networkTimeMs 请求耗时

内部 Response 转换流程图
volley response

CacheDispatcher

Provides a thread for performing cache triage on a queue of requests.

CacheDispatcher类是一个线程类,里面有四个比较重要的变量:
1、BlockingQueue<Request<?>> mCacheQueue来自缓存的队列。
2、BlockingQueue<Request<?>> mNetworkQueue将会访问网络的队列。
3、Cache mCache缓存的处理类。
4、ResponseDelivery mDelivery回调响应的类。

CacheDispatcher.run()

在线程被创建后马上就被执行了。里面首先会对缓存路径进行初始化工作。接着就进入一个无限的循环等待里。

首先会从缓存队列出取出请求,如果队列为空则会一直阻塞直到队列有数据,接着尝试从缓存当中取出响应结果,如何为空的话则把这条请求加入到网络请求队列中,如果不为空的话再判断该缓存是否已过期,如果已经过期了则同样把这条请求加入到网络请求队列中,否则就认为不需要重发网络请求,直接使用缓存中的数据即可。之后会调用Request的parseNetworkResponse()方法来对数据进行解析,最后就是将解析出来的数据进行回调了。

CacheDispatcher 流程图
NetworkDispatcher

NetworkDispatcher

Provides a thread for performing network dispatch from a queue of requests.

NetworkDispatcher类是一个线程类,里面有四个比较重要的变量:
1、BlockingQueue<Request<?>> mQueue响应来自服务的队列。
2、Network mNetwork 网络进度请求。
3、Cache mCache缓存的处理类。
4、ResponseDelivery mDelivery回调响应的类。

NetworkDispatcher.run()

在线程被创建后马上就被执行了。首先进入一个无限的循环等待里。等待任务进来。

在接受到请求后会直接访问网络发送请求,请求处理结束则将结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。

NetworkDispatcher 流程图
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。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容