概述
今天复习了下Volley,刚好这几天一直在学习源码,然后参考了网上的文章,以在最后回调onResponse和onErrorResponse为目的去学习它。关于它的用法可以看这一篇。
它的流程大致是这样的:创建请求>存放到当前请求队列>判断是否有相同的请求,有则存放到暂存队列,无则到网络请求队列;在暂存队列中进入下一步是放到缓存队列,判断状态>是否需要重新到网络队列最后是响应请求。
开始
这是一个最基础的用法。
//1.创建一个请求队列
final RequestQueue request = Volley.newRequestQueue(MainActivity.this);
//2.创建一个请求
StringRequest stringRequest = new StringRequest(url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
tv_result.setText(response);
}
}, new Response.ErrorListener() {//发生异常
@Override
public void onErrorResponse(VolleyError error) {
tv_result.setText("加载错误");
}
});
//3.将创建的请求添加到请求队列中去
request.add(stringRequest);
//取消请求
request.cancelAll("take");
进入newRequestQueue(MainActivity.this):创建RequestQueue
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
......
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
//使用HttpUrlConnection
stack = new HurlStack();
} else {
//使用HttpClient
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
//在此创建RequestQueue
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
HurlStack和HttpClientStack,他们分别是对应HttpUrlConnection和HttpClient。看其他文章才知道这是由于在api9之前,HttpUrlConnection的BUG多,但在API9以后,由于HttpUrlConnection的BUG修复了很多,加上他是一个轻量型的框架,所以在API9之后使用HttpUrlConnection。
RequestQueue的构造方法
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;//缓存策略
mNetwork = network;//网络
mDispatchers = new NetworkDispatcher[threadPoolSize];//调度器,默认的threadPoolSize为4
mDelivery = delivery;//分发器
}
RequestQueue的start方法
public void start() {
stop();
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
//开启缓存调度,它是一个Thread
mCacheDispatcher.start();
// Create network dispatchers (and corresponding threads) up to the pool size.
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
//开始网络调度
networkDispatcher.start();
}
}
以下是缓存调度和网络调度
CacheDispatcher的run方法
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
//设置优先级(后台)
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Make a blocking call to initialize the cache.
mCache.initialize();//初始化缓存
while (true) {//进入循环
try {
//从队列中获取请求
final Request<?> request = mCacheQueue.take();//阻塞操作
request.addMarker("cache-queue-take");//标记请求为缓存suo
//如果请求取消了,就不进行分发或者发送请求
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
//根据缓存的key去找磁盘上的缓存,它的key是url
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
//说明不存在,标记为miss,然后加入网络队列,进行新的循环
request.addMarker("cache-miss");
mNetworkQueue.put(request);
continue;
}
//检查是否过期
if (entry.isExpired()) {
//说明已经过期了,标记为expired,加入网络队列,进行新的循环
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
//到这里说明缓存可用
request.addMarker("cache-hit");
//解析响应
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
//不需要刷新,直接分发响应
mDelivery.postResponse(request, response);
} else {
//需要刷新,因为是一个软过期请求,所以进行分发然后也要进行发送请求。
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;
//先分发,然后在加入网络队列中去刷新
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
}
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {//退出
return;
}
continue;
}
}
}
小结:
- 开启缓存调度,进入一个循环,从缓存队列中拿取请求;此队列是一个阻塞队列。
- 判断请求的状态,如果是根据请求的key(url)获取对应的实体,从实体中获取状态。
- 如果为null,那么直接放入网络请求中去。
- 如果是过期,那么放入到请求中的cacheEntry,然后放入到网络请求中去
- 如果不为null也不过期,那么解析获取响应,然后进一步判断是否需要刷新,需要的话则进行分发响应并且发送网络请求,不需要刷新的话则直接分发响应。
NetworkDispatcher的run方法
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();//开始时间,在后面如果出现异常可以用到
Request<?> request;
try {
// Take a request from the queue.
request = mQueue.take();//一样是阻塞操作
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
try {//标记为网络队列操作
request.addMarker("network-queue-take");
// If the request was cancelled already, do not perform the
// network request.
//如果请求被取消了,就finish掉,进行新的循环
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
// Perform the network request.
//响应请求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");//标记为完成响应
// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
//根据英文注释,如果该此响应已经存在就不再分发了,将此请求finish掉,重新循环
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
//解析响应
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
//判断是否需要缓存
if (request.shouldCache() && response.cacheEntry != null) {
//缓存请求
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// Post the response back.
request.markDelivered();
//分发响应
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
//分发错误响应
mDelivery.postError(request, volleyError);
}
}
}
Network的performRequest(request),Network是BasicNetwork
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = Collections.emptyMap();
try {
//以下过程是将状态、响应码、实体、响应头等封装到NetworkResponse中并返回
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
//真正发起网络请求的地方
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
//添加响应头信息
entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// Some responses such as 204s do not have content. We must check.
if (httpResponse.getEntity() != null) {
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// Add 0 byte response as a way of honestly representing a
// no-content request.
responseContents = new byte[0];
}
// if the request is slow, log it.
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
SystemClock.elapsedRealtime() - requestStart);
}
//删减代码,主要是做一些异常处理
.......
}
}
}
从以上可知最终发起网络请求的是mHttpStack,也就是最开始由版本判断的,大于9里面使用HttpUrlConnection,小于9使用HttpClient,然后将获取到的HttpResponse包含的信息封装到NetworkResponse中并返回。
小结:
- NetworkDispatcher同样是进入循环,然后从网络队列中拿出请求,这次只判断请求是否取消了。
- 然后通过Network,它是BasicNetwork对象,通过mHttpStack获取响应;
- 然后判断是否已经分发了该响应,如果没有,解析响应后判断此请求是否需要缓存,如果需要便缓存起来。
- 最后进行分发响应结果。
RequestQueue的add方法
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) {
//获取该请求对象对应的缓存的key
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
//说明该key对应的请求队列存在(有相同的请求正在处理)
// 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 {
// 插入一个null队列,表示当前有请求正在执行
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
小结 RequestQueue里面有两个优先级阻塞队列,也就是PriorityBlockingQueue,一个是放网络的,另一个是放缓存的;还有一个Map集合mWaitingRequests,用来管理请求,即如果有同类请求正在处理或等待,则将该请求放到等待队列中,否则标记为没有同类请求。
走了这么多,还是看不到在哪里回调请求的结果。那么回头看看RequestQueue的构造方法吧
RequestQueue中的一个构造方法
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
可知其中的ExecutorDelivery对象的构造参数是一个Handler,并且是在主线程中的,该构造方法还可以限制线程数。
ExecutorDelivery
public ExecutorDelivery(final Handler handler) {
// 创建一个里面用于处理任务的执行器
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
//这个Handler就是传进来的主线程Handler;将任务发送至主线程中的Handler
handler.post(command);
}
};
}
分发响应结果的最终方法
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
//mResponsePoster在构造方法中已经初始化了,也就是将响应任务传给主线程的Handler,让Handler发送
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
进入ResponseDeliveryRunnable
它是一个实现Runnable的类,以下为它的run方法
@Override
public void run() {
// 如果请求被取消了,则不进行分发
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
//根据响应结果分发内容或者是响应的失败
if (mResponse.isSuccess()) {
//响应成功
mRequest.deliverResponse(mResponse.result);
} else {
//响应失败
mRequest.deliverError(mResponse.error);
}
// 做标记是否为中间响应或者完成
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();
}
}
}
到这里,就快接近能够看到成功和失败的回调了,也就是这两个方法
if (mResponse.isSuccess()) {
//响应成功
mRequest.deliverResponse(mResponse.result);
} else {
//响应失败
mRequest.deliverError(mResponse.error);
}
由于这里mRequest是抽象的,进到继承它的StringRequest类中,看deliverResponse()方法:
@Override
protected void deliverResponse(String response) {
mListener.onResponse(response);
}
然后在Request中看deliverError()方法:
public void deliverError(VolleyError error) {
if (mErrorListener != null) {
mErrorListener.onErrorResponse(error);
}
}
然后看看其他的方法
Request的finish()方法
void finish(final String tag) {
if (mRequestQueue != null) {
//调用请求队列的finish
mRequestQueue.finish(this);
}
if (MarkerLog.ENABLED) {
final long threadId = Thread.currentThread().getId();
//向主线程发送消息
if (Looper.myLooper() != Looper.getMainLooper()) {
Handler mainThread = new Handler(Looper.getMainLooper());
mainThread.post(new Runnable() {
@Override
public void run() {
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
});
return;
}
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
}
RequestQueue中的finish方法
<T> void finish(Request<T> request) {
// Remove from the set of requests currently being processed.
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
}
synchronized (mFinishedListeners) {
for (RequestFinishedListener<T> listener : mFinishedListeners) {
//移除请求时的回调
listener.onRequestFinished(request);
}
}
if (request.shouldCache()) {
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
......
mCacheQueue.addAll(waitingRequests);
}
}
}
}
RequestQueue中的finish方法首先将完成的请求从当前请求队列中移除,然后遍历RequestFinishedListener回调onRequestFinished(request)方法;最后被移除的暂存请求队列如需要缓存的话则添加到缓存队列中。
最后是RequestQueue的cancelAll();
public void cancelAll(final Object tag) {
if (tag == null) {
throw new IllegalArgumentException("Cannot cancelAll with a null tag");
}
cancelAll(new RequestFilter() {
@Override
public boolean apply(Request<?> request) {
return request.getTag() == tag;
}
});
}
public void cancelAll(RequestFilter filter) {
synchronized (mCurrentRequests) {
for (Request<?> request : mCurrentRequests) {
if (filter.apply(request)) {
request.cancel();
}
}
}
}
# Request类
public void cancel() {
mCanceled = true;
}
其中通过过滤来进行取消。
到此,一个流程基本就结束了(除了具体的发起网络请求和具体的获取响应)。