Volley:
Volley采用了典型的门面模式,一般的框架广泛采用这种模式,只提供一个或几个门面类,这样方便第三方去学习;而所谓的门面模式就是他负责自己封闭系统和外界系统的交互,而不允许外部系统直接和内部系统进行交互,所有的交互都通过这个门面来完成。
Volley的工作流程:
(1) 第一步创建初始化一个RequestQueue队列(注意该队列是一个线程阻塞的队列,即BlockQueue)
在创建这个队列的时候它进行了一系列的初始化工作:
1.
File
cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);默认是5M,地址为apk的cache文件里面
2.创建Client即管理网络的栈
stack
= new
HurlStack
();
3.创建网络任务 Network network = new BasicNetwork(stack);
4.创建工作队列RequestQueue:
queue
=
new
RequestQueue
(
new
DiskBasedCache
(cacheDir
,
maxDiskCacheBytes
)
,
network)
;
这个过程中把任务和缓存的引用交给了
RequestQueue方便RequestQueue进行下一步操作
(2)
开启队列循环
queue
.start
()
;
在这个过程中它又进行了如下操作:
CacheDispatcher mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
创建一个缓存线程:用来分发缓存任务;并开启运行它;
创建四个(默认也可以通过new不同的构造进行设置)net线程并开启
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
这个开启运行的过程是关键所在,我们看它做了哪些工作:
首先缓存线程:
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 设置线程优先级为后台线程
核心代码如下:
while
(
true
) {
final
Request<?>
request
=
mCacheQueue.take
()
;
逻辑判断是否需要刷新过期等等;然后如果不能用缓存则把加入到net线程之中,如果是不需要刷新
且没有过期那么就把把分配给
ResponseDelivery
进行处理
}
在这个过程中,由于
mCacheQueue是实现了
BlockingQueue的,
因而它拿取的时候是处于一个线程
阻塞的状态的。
研究下它是怎么实现线程阻塞的:
他这里对应的实现类是
PriorityBlockingQueue,我们看一下它take方法得源码:
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
E result;
try {
while ( (result = dequeue()) == null)//在这里进行了一个循环如果为空不断的取出queue里的元素如果最后为空,那
notEmpty.await();
//
么就让等待,这里面notEmpty是一个Condition,这是JDK1.5以后推出的用来代替
} finally {
//
Object的wait和notify()方法,使用awit()和singal()方法,操作并发更加的高效和
lock.unlock();
//
安全,废话好多,其实也就是说这里当这个queue里没有元素的时候它是处于wait状态
}
//
的,当wait时线程会释放资源
return result;
}
在队列循环过程中它是获取缓存文件中内容转化为实体(以url为键标示的),这理应是在net线程里进行处理的;
(3)开启net线程
net线程也是利用blockqueue进行线程阻塞的;
网络请求的代码是通过以下进行的:
NetworkResponse networkResponse = mNetwork.performRequest(request);
而
mNetwork是在创建队列时传进去的,
Network
network
=
new
BasicNetwork
(
stack
)
;
最终调用的是httpResponse = mHttpStack.performRequest(request, headers);
这个mHttpStack其实就是
stack
=
new
HurlStack
()
;(SDK9以上采用这个,SDK9以下使用
HttpClientStack
)
这里发现个这个:
map.putAll(additionalHeaders);
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
这是相当于转换URL的,我们可以通过在创建requestqueue时将之传入,并复写其rewriteUrl的方法来转换url地址
最终访问网络的实现是这样:将最终结果封装在了
BasicHttpResponse
里面,它这个封装的过程是很漂亮的
URL
parsedUrl
=
new
URL
(url)
;
HttpURLConnection connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response
.setEntity
(
entityFromConnection
(connection))
;
for
(
Entry<String,
List<String>>
header
:
connection
.getHeaderFields
()
.entrySet
()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
下面是它封装实体的部分:
private
static
HttpEntity
entityFromConnection
(
HttpURLConnection
connection
) {
BasicHttpEntity entity = new BasicHttpEntity();
InputStream inputStream;
try {
inputStream = connection.getInputStream();
} catch (IOException ioe) {
inputStream = connection.getErrorStream();
}
entity.setContent(inputStream);
entity.setContentLength(connection.getContentLength());
entity.setContentEncoding(connection.getContentEncoding());
entity.setContentType(connection.getContentType());
return entity;
}
访问完成封装好Response以后;打标记,根据request是否需要缓存来决定是否缓存到缓存文件里面,
分发:mDelivery.postResponse(request, response);
(4)分发的操作
mDelivery 也是在构建RequestQueue时创建的; 默认情况下只是
new ExecutorDelivery(new Handler(Looper.getMainLooper()),可以看出采用getMainLooper方法也是可以在子线程创建Handler
来刷新UI的而不是仅仅只能在主线程创建Handler;
分发的具体实现
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
public
void
run
() {
// If this request has canceled, finish it and don't deliver.
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// Deliver a normal response or error, depending.
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
// If this is an intermediate response, add a marker, otherwise we're done
// and the request can be finished.
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
// If we have been provided a post-delivery runnable, run it.
if (mRunnable != null) {
mRunnable.run();
}
}
看一下它是怎么实现优先级这个功能的,requestqueue每次取出request时都调用的是这个方法
private E dequeue() {
int n = size - 1;
if (n < 0)
return null;
else {
Object[] array = queue;
E result = (E) array[0];
E x = (E) array[n];
array[n] = null;
Comparator<? super E> cmp = comparator;//这是核心通过比较器进行了排序
if (cmp == null)
siftDownComparable(0, x, array, n);
else
siftDownUsingComparator(0, x, array, n, cmp);
size = n;
return result;
}
}
(5)添加请求任务
public
<T>
Request<T>
add
(
Request<T>
request
) {
// Tag the request as belonging to this queue and add it to the set of current requests.
request.setRequestQueue(this);
synchronized (mCurrentRequests) { .
//这里进行了线程同步
mCurrentRequests.add(request);
}
// Process requests in the order they are added.
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// If the request is uncacheable, skip the cache queue and go straight to the network.
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
// Insert request into stage if there's already a request with the same cache key in flight.
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
// There is already a request in flight. Queue up.
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<Request<?>>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else {
// Insert 'null' queue for this cacheKey, indicating there is now a request in
// flight.
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
(6)缓存策略:
主要在
HttpHeaderParser里面进行的设置