序
wuliVolley
请求框架出来已经许久许久,小白的我,以前只知其一,而不知其二。 现在才开始学习他的源码,惭愧惭愧,既然都落后了,当然得乘胜追击学起来,否则只能永远落后了(放了一波毒鸡汤)。希望能学习其中的编程思想以及了解它是如何完成整个请求以及实现磁盘缓存的。本文我将通过一个分析StringRequest
的请求来完整的剖析一下其中涉及到的重要代码。我的初衷一直都没有变,那就是每写一篇文章是为了给自己的知识库添上一点,也是让自己坚持学习,同时给学习路上的你我他提供一点参考的资料。 本文将会有点长,别失去耐心,好好看下去。我相信肯定能让你对Volley
更了解。 Are you Ready? Go!
要学习一个源码,我们可以首先从我们使用的地方出发去学习,这样更好理解,也不至于一下子迷失在源码的汪洋大海中。所以我们先看看我们怎么用它。当请求一个string的时候,我们会:
// 1.new一个RequestQueue
RequestQueue mRequestQueue = Volley.newRequestQueue(context);
//2.new一个StringRequest对象,传入Response.Listener 和Response.ErrorListener来监听请求成功和请求失败的时候的回调
StringRequest request = new StringRequest("http://www.baidu.com", new Response.Listener<String>() {
@Override
public void onResponse(String s) {
txt_response.setText(s);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Log.e("onError", volleyError.getMessage());
}
});
//将请求加入到请求队列中
mRequstQueue.add(request);
RequestQueue
正如它的字面意思一样就是一个请求队列。每当有一个新的请求的时候就将请求添加进去。让我们看看Volley.newRequstQueue
做了些什么。
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, (HttpStack)null);
}
可以看到它调用了另外一个方法,传入了一个null的HttpStack,这个是什么呢?
Volley提供了HttpStack
的两个实现子类,HttpClientStack 与 HurlStack HttpClientStack 和HurlStack是两种网络请求的封装类.当Android的SDK版本号小于9的时候,使用的是HttpStack来进行网络请求,而HttpClientStack是通过HttpClient来请求网络数据的。相反,当sdk大于9时,使用HurlStack来请求网络数据,HurlStack是通过HttpURLConnection来进行网络连接并请求数据的。具体是如何请求数据的这里我们就不讲解了,简单提一下就是通过HttpClient
或者HttpUrlConnection
来请求数据获得Response
. 感兴趣的可以自己在源码中找到对应的类查看一下。
接着往下看:
/**
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
*
* @param context A {@link Context} to use for creating the cache dir.
* @param stack An {@link HttpStack} to use for the network, or null for default.
* @return A started {@link RequestQueue} instance.
*/
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
//定义一个缓存目录,默认是在应用的缓存目录下创建一个叫volley的目录用于存储缓存
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
//如果stack是null,则根据SDK的版本来创建对应的HttpStack的子类
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack(); //通过HttpUrlConnection请求
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
//通过HttpClient连接请求网络数据
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
//传入HttpStack,创建一个Network对象,在我看来Network类是对HttpStack的进一步封装,添加了对请求结果的一些处理,实际网络请求是通过HttpStack实现的。
Network network = new BasicNetwork(stack);
//创建并启动一个RequestQueue
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
我已经在代码中添加了对应的注释,主要就是创建了HttpStack
对象传递给Network
的构造函数,然后用于网络请求,之后通过Network
以及DiskBasedCache
构造一个RequestQueue
并start
。在这里DiskBasedCache
是一个默认的管理磁盘缓存的一个类,用于缓存请求的结果。Volley
的缓存机制就是通过这个实现的,****需要注意的是因为Volley的缓存都是缓存在磁盘里面的所以缓存读取速度要比内存缓存更慢****。当然你也可以改成自己要的内存缓存,这也是我们学习源码的一个目的,不仅知道别人是怎么写的,更重要的是能够灵活运用并且将其改造成我们需要的样子。
接下来我们来看一下RequestQueue
的构造函数和start
都做了些什么,为什么start
之后,然后只需要将Request
加入队列就开始请求数据并且得到对应的Response
。
通过追踪代码,可以知道最终调用了这个构造函数。
/**
创建一个工作池,只有start方法调用了之后,进程才会开始
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @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
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
在上面的英文注释当中已经说得很清楚了,Cache
就是用于将Http请求的Response
存储到磁盘,Network
用于执行Http请求。NetworkDispatcher
是一个线程,用于网络请求的分发。这里我们默认是开启了4个NetworkDispatcher
.ResponseDelivery
是用于分发请求的错误和响应的。
看完了构造函数我们就来看看start()
方法吧。
/**
* Starts the dispatchers in this queue.
*/
public void start() {
//首先将确保所有CacheDispatcher和NetworkDispatcher都停止
stop(); // Make sure any currently running dispatchers are stopped.
//接下来开启CacheDispatcher和NetworkDispatcher线程
// Create the cache dispatcher and start it.
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
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
和NetworkDispatcher
线程,然后开始了监听网络请求的工作。那么Cache Dispatcher
和NetworkDispatcher
分别做了哪些工作呢。
这里我们先来看看NetworkDispatcher
,上面我们说到过它是一个线程,所以我们先看看它的构造方法,然后直击他的run方法。
/**
* Creates a new network dispatcher thread. You must call {@link #start()}
* in order to begin processing.
*
* @param queue Queue of incoming requests for triage
* @param network Network interface to use for performing requests
* @param cache Cache interface to use for writing responses to cache
* @param delivery Delivery interface to use for posting responses
*/
public NetworkDispatcher(BlockingQueue<Request<?>> queue,
Network network, Cache cache,
ResponseDelivery delivery) {
mQueue = queue;
mNetwork = network;
mCache = cache;
mDelivery = delivery;
}
从上面可以知道,NetworkDispathcer
拥有一个BlockingQueue
阻塞式队列的一个引用。用于存储请求,还有Network, cache, ResponseDelivery
这几个对象的作用,我在上面有提到过,不记得了可以向上滑动一下回忆一下。接下来我们看看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. 调用mNetwork的performRequest,请求数据
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
//对返回的NetworkResponse进行检测,如果notModified并且已经将结果分发过了(返回给用户了),直接finish
// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// Parse the response here on the worker thread.
//将请求转换成Response对象,parseNetworkResponse时对应的Request的子类实现的
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// Write to cache if applicable.
// TODO: Only update cache metadata instead of entire record for 304s.
if (request.shouldCache() && response.cacheEntry != null) {
//如果需要缓存并且cacheEntry不等于空就将本次请求,添加到缓存中
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// Post the response back. 标记已经将Response分发给Delivery(即回调给Request了)
request.markDelivered();
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
//如果出错了则回调onError方法
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);
}
}
}
在代码中,我已经在需要注释的地方,添加了详细的注释了。所以我们讲一下大概的,NetworkDispatcher
的run
方法在一个无限循环中不断的从请求队列中获取Request
,然后调用mNetwork.performRequest
请求网络数据。这里的mNetwork是一个BasicNetwork对象,那么我们就插播一个BasicNetwork
的源码分析,从而看看mNetwork.performRequest(request)
究竟都干了什么。
BasicNetwork
这部分主要是对BasicNetwork
的源码的解析。那BasicNetwork
是用来干什么的呢,BasicNetwork
在我的理解中就是通过HttpStack
来执行一个Volley request
的。我们挑重点的代码来讲解一下,BasicNetwork
主要干了些什么。
首先我们看BasicNetwork
的定义和构造函数:
/**
* A network performing Volley requests over an {@link HttpStack}.
*/
public class BasicNetwork implements Network {
/**
* @param httpStack HTTP stack to be used
*/
public BasicNetwork(HttpStack httpStack) {
// If a pool isn't passed in, then build a small default pool that will give us a lot of
// benefit and not use too much memory.
this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
}
/**
* @param httpStack HTTP stack to be used
* @param pool a buffer pool that improves GC performance in copy operations
*/
public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
mHttpStack = httpStack;
mPool = pool;
}
让我一起来看一下,BasicNetwork
继承自Network
,那么Network
里面定义了什么呢?
/**
* An interface for performing requests.
*/
public interface Network {
/**
* Performs the specified request.
* @param request Request to process
* @return A {@link NetworkResponse} with data and caching metadata; will never be null
* @throws VolleyError on errors
*/
NetworkResponse performRequest(Request<?> request) throws VolleyError;
}
可以看到Network
只是定义了一个抽象方法提供给子类实现,从方法的注释来看performRequest
就是执行了一个请求并且返回NetworkResponse
即网络请求的响应,具体怎么请求的由其子类的实现决定。好,从这里我们就可以知道performRequest
是我们请求网络最关键的一个方法。
继续看BasicNetwork
的构造方法,传入了两个参数,第一个参数HttpStack
是一个封装了网络请求的类,其实BasicNetwork
的网络请求的真正的执行是调用了HttpStack
来实现的,在上一小节我们提到过。
第二个参数是一个ByteArrayPool
,这个类是android提供的用于避免重复创建byte[]的一个缓存机制,类似于LruCache。减少堆内存的消耗,提高性能。
PerformRqeust
接下来就来看看BasicNetwork
的performRequest
方法是如何请求网络数据并且返回响应,以及如何处理的。
@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 {
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
//根据缓存来设置请求头的一些值 request.getCacheEntry() 返回一个request的缓存实体
addCacheHeaders(headers, request.getCacheEntry());
//发送一个请求并且得到相应的HttpResponse
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) {
//当一个网络请求返回的响应码是304的时候,说明该url对应的资源没有改变,所以可以直接从缓存中获取上次缓存的数据
Entry entry = request.getCacheEntry();
if (entry == null) {
//如果entry为空的话,直接将响应头传入NetworkResponse
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
//当响应码是304时,服务器的响应头里面会有一些新的信息,所以需要更新缓存中的响应头信息,比如新的过期时间等
// A HTTP 304 response does not have all header fields. We
// have to use the header fields from the cache entry plus
// the new ones from the response.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
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) {
//将HttpEntity转换成对应的byte[]数组
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);
} catch (SocketTimeoutException e) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException e) {
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
int statusCode;
if (httpResponse != null) {
statusCode = httpResponse.getStatusLine().getStatusCode();
} else {
throw new NoConnectionError(e);
}
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
NetworkResponse networkResponse;
if (responseContents != null) {
networkResponse = new NetworkResponse(statusCode, responseContents,
responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
statusCode == HttpStatus.SC_FORBIDDEN) {
attemptRetryOnException("auth",
request, new AuthFailureError(networkResponse));
} else if (statusCode >= 400 && statusCode <= 499) {
// Don't retry other client errors.
throw new ClientError(networkResponse);
} else if (statusCode >= 500 && statusCode <= 599) {
if (request.shouldRetryServerErrors()) {
attemptRetryOnException("server",
request, new ServerError(networkResponse));
} else {
throw new ServerError(networkResponse);
}
} else {
// 3xx? No reason to retry.
throw new ServerError(networkResponse);
}
} else {
attemptRetryOnException("network", request, new NetworkError());
}
}
}
}
在上面的代码中我自己添加了比较详细的注释了。我们两一起看看,我觉得比较重要的几个方法。 在我们请求之前我们调用了addCacheHeaders(headers, request.getCacheEntry());
来给请求头添加了一些参数。 跳进addCacheHeaders
方法来看看它做了什么!!
private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {
// If there's no cache entry, we're done.
if (entry == null) {
return;
}
//初始化"If-None-Match"以及"If-Modified-Since"两个变量可以用于之后 判断是否服务器上的数据是否改变了
if (entry.etag != null) {
headers.put("If-None-Match", entry.etag);
}
if (entry.lastModified > 0) {
Date refTime = new Date(entry.lastModified);
headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
}
}
方法很简单首先判断一下缓存是否是空的,空的直接返回,不是空的话,取出entry.etag
和entry.lastModified
分别赋值给请求头的If-None-Match
和If-Modified-Since
.为什么要这样做呢,在网络请求的时候,一般当我们的请求有缓存的时候并且我们本地缓存过期的时候,因为缓存期过了,并不代表服务器上面的资源就一定变了。所以我们需要判断一下服务器上面的资源是否变了。那么如何判断呢?
这个时候就通过在请求中添加上一次缓存响应中的响应头的中的If-Modified-Since
和If-None-Match
到请求响应头中,并传递给服务器。服务器接收到这个请求头的时候就会根据这两个变量判断一下资源是否改变。
那么If-Modified-Since
和If-None-Match
分别代表了什么呢。
Last-Modified
标记了资源文件在服务器的最后修改时间,当客户端由于缓存过期发起请求时,请求头要使用If-Modified-Since头部,它的值就是第一次服务器返回的Last-Modified。服务器收到这个时间后,跟当前的资源文件最后修改时间进行对比,如果服务器中资源文件的最后修改时间新与Last-Modified的值,那么说明资源文件进行修改过。
Etag
又是什么呢,它是一个资源文件的标示,同样的当客户端由于缓存过期发送请求时, 请求头把上一次服务器返回的Etag
传入请求头,服务器接收到之后,与当前资源的Etag
对比,如果不一样了,则说明资源改变了。所以通过这两个变量就可以判断服务器的资源到底有没有变。
服务器通过上面两个变量判断资源是否改变了。如果没有改变则返回304,并返回新的响应头里面包含新的过期时间。如果改变了则返回200响应码.
addCacheHeaders
之后就可以执行请求了,通过httpStack
获得HttpResponse
,根据返回响应码,做相应的处理。
如果StatusCode
是304,则说明服务器上面的资源没有改变,则直接从缓存中拿数据,如果entry
为空则直接返回一个data为null,headers==Response的headers的NetworkResponse
。 但是如果entry
不是空的话,则需要更新entry
的headers
更新过期时间等。
如果返回的响应码statusCode > 200 && statusCode < 299
,则 将HttpEntity转换成bytes数组作为响应内容,并直接返回NetworkResponse
,否则抛出IOException
然后做相应的处理,最终的结果要么返回NetworkRespons
,要么抛出VolleyError
类型的异常,然后调用出根据返回值,来回调对应listener的方法。接下来我就不继续讲了。相信聪明的你们都能把剩下的看懂的。_!!
接着我们就返回到NetworkDispatcher
的run方法继续向下看,现在我们知道mNetwork.performRequest(request)
这句话都干了些什么,虽然代码有点多,但是其实就是获得了一个NetworkResponse。吓往下看。为了不用来回拉上去看,我把剩下的代码再贴一次。
```
//对返回的NetworkResponse进行检测,如果notModified并且已经将结果分发过了(返回给用户了),直接finish
// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// Parse the response here on the worker thread.
//将请求转换成Response对象,parseNetworkResponse时对应的Request的子类实现的
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// Write to cache if applicable.
// TODO: Only update cache metadata instead of entire record for 304s.
if (request.shouldCache() && response.cacheEntry != null) {
//如果需要缓存并且cacheEntry不等于空就将本次请求,添加到缓存中
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// Post the response back. 标记已经将Response分发给Delivery(即回调给Request了)
request.markDelivered();
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
//如果出错了则回调onError方法
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);
}
```
得到了NetwokResponse
之后,我们需要对返回的请求结果检测和处理之后,传递给mDelivery
,最后反馈掉调用的请求的Listener中。
首先判断一下networkResponse.notModified
是否为true,如果为true,则说明服务器上的资源没有改变,并且request.hasHadResponseDelivered()
这个请求的请求结果已经分发过了,那么就将这个请求finish掉,进行下一次请求。上面两个条件不同时符合,那么调用request.parseNetworkResponse(networkResponse);
将NetworkResonse
转换成Response
那么parseNetworkResponse
这里方法对Response做了什么呢。看看Request的代码我们可以知道,parseNetworkResponse
是一个由子类继承实现的一个方法,终于我们又可以看回StringRequest
了,我们看看StringRequest
方法做了些什么。
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
可以看到它将response转换成了String类型的Response,所以其实这个方法就是将NetworkResponse
转换成了对应数据类型。拿到了Respnse<String>之后我们就可以通过mDelivery.postResponse(request, response);或mDelivery.postError(request, volleyError);
将这个响应,通过StringRequst的deliverResponse方法,然后通过mListener将结果回调出去。 至此一次StringRequst请求我们就分析完了一半了。一起来看看mDelivery
做了什么。
这里mDelivery
是一个ExecutorDelivery对象,为什么?见下:
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
并且handler的消息处理都是在主线程哦。因为new Handler(Looper.getMainLooper())
.
那就让我们一起来看看 ExecutorDelivery这个类吧。
首先PO一个构造函数。
/** Used for posting responses, typically to the main thread. */
private final Executor mResponsePoster;
/**
* Creates a new response delivery interface.
* @param handler {@link Handler} to post responses on
*/
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
用了一个Executor异步执行框架用来封装handler。然后看看postResponse
和postError
方法。
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
@Override
public void postError(Request<?> request, VolleyError error) {
request.addMarker("post-error");
Response<?> response = Response.error(error);
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
}
可以看到最终都将Response和Reqeust传递到了一个ResponseDeliveryRunable
里面然后执行。快马加鞭,我们看看ResponseDeliveryRunable的run方法。
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();
}
}
注意在它处理请求之前它又一次判断了请求是否被取消了,果然官方的代码有一些地方就是比较谨慎。记得在NetworkDispatcher
的run方法的时候就已经判断过一次了,这样多次判断可以避免在请求返回之前用户取消了。我们应该也学着这么谨慎,从而避免一些不必要的处理。如果Request没有被取消掉那么就通过mResponse.isSuccess()
判断一下请求是否成功,那么它如何判断请求是否成功的呢。
/**
* Returns whether this response is considered successful.
*/
public boolean isSuccess() {
return error == null;
}
原来他只是简单的根据error是否为空来判断请求成功了没有,你可以跟踪一下代码,就知道当请求成功时候error是null的,失败的时候error是一个VolleyError对象buweinull。如果成功了就调用mRequest.deliverResponse(mResponse.result);
,然后就成功的请求到数据啦。Request
的deliverResponse
方法如下。
@Override
protected void deliverResponse(String response) {
mListener.onResponse(response);
}
至此我们就将请求结果回调到一开始new Request的listener里面啦。我们对StringRequest的请求过程的分析,也分析到一半了。为什么是一半呢。因为CacheDispatcher
和RequestQueue.add
还没有分析呢,这也是很重要的知识点。 由于篇幅原因,我分两篇文章来写。下一篇,马上就会发布的,敬请期待!!
一口气写了这么多,累了。闪了。 有问题欢迎留言。