0x2、并发设计
像上面HttpUrlConnection的例子,每发起一次请求,都新起一个线程,太蠢了:
- 没复用线程,频繁创建销毁线程造成不必要的开销;
- 没对最大线程数做一个限制,可能会造成过度资源竞争,系统使用率不高;
所以涉及到并发的开源项目,线程池 基本是没得走的了,再配个 任务队列 进出队列操作加锁,有时为了解耦,还会再拆出来一个 调度器,死循环 访问 任务队列,取出任务交由线程池执行。
Volley是支持并发的,看下它是如何设计的,没有找到线程池初始化代码,倒是找到了 两个线程实现类:
NetworkDispatcher
继承 Thread类,所以这里默认定义了 容量为4的线程数组,另外一个线程 CacheDispatcher
。它们都在 RequestQueue → start()
中启动:
跟下start()的调用处 Volley → newRequestQueue
:
就是实例化RequestQueue的时候,就创建并开启了这些线程。
0x3、请求调度设计
调用 RequestQueue → add()
请求入队,跟下具体实现:
用到三个集合,根据对应注释推演出各种用途:
- mCurrentRequests : HashSet → 正在处理的所有请求(包括等待和处理中)的集合;
- mNetworkQueue : PriorityBlockingQueue → 非缓存请求队列;
- mCacheQueue : PriorityBlockingQueue → 缓存请求队列;
入队比较简单,接着看下具体的调度流程,不难看出分成两类,走缓存(默认)和不走缓存,先看前者~
① 不走缓存的请求
NetworkDispatcher → run()
死循环拿队列的请求,处理请求后的结果流向有四个:
- 请求处于取消状态 → request.finish("network-discard-cancelled");
- 304且响应已解析过 → request.finish("not-modified");
- 正常结束 → mDelivery.postResponse(request, response);
- 异常结束 → mDelivery.postError(request, volleyError);
先跟下 Request → finish()
:
比较简单:移除集合中的请求,打日志,调用下请求结束的回调,接着看下后两个,跟下 ExecutorDelivery
跟下 mResponsePoster
初始化部分代码:
好吧,就是一个 执行器
,负责将任务分发出去,因为后续操作在主线程操作,所以得用 Handler。
接着,看下任务具体做了啥 ResponseDeliveryRunnable → run()
正常请求的流转还是很好理解的,接着看下走缓存请求的调度过程~
② 走缓存的请求
直接跟 CacheDispatcher → run()
,同样是死循环,直接看 processRequest()
部分代码:
简单点说:有缓存且缓存没过期直接返会,其他情况把请求加到请求队列中。接着再看看缓存这块是咋设计的~