之前用过一次volley框架,觉得十分好用,在与服务器交互时十分方便,尤其在图像异步加载,传递json数据,缓存等,其在网络通信方面表现的极为出色,适合数据量不大,但请求频繁的应用,如加载图片。
volley使用一般步骤为:
1.通过volley的静态方法newRequestQueue()创建RequestQueue请求队列
2.创建request对象,volley官方提供了几种已经定义好的request类型。
一是:StringRequest用于请求String数据,服务器会返回String类型数据.
二是:JsonRequest是抽象类,他有两个子类:JsonObjectRequest和JsonArrayRequest。前者请求json数据,后者请求json数组.
三是:ImageRequest用于加载网络图片。
当然由于volley框架可扩展性好,你也可以自定义request来实现你的需求.
3.利用requestQueue的add()方法将request加入requestQueue队列中.
以上是volley框架使用过程,接下来我们好好看看volley框架的源码.
先看下官方给的volley框架工作流程图.
我们直接从字面意思上来理解,第一步把request添加到cachequeue缓存队列中,然后通过CacheDispatcher来取出request来处理,然后看缓存结果是否有效,有效则直接读取结果并解析,然后分发回主线程处理。否则,将通过NetworkDispatcher来分发,然后将网络返回结果进行解析,再返回到主线程处理。
接下来我们来逐步分析代码,首先通过volley.newRequestQueue()方法创建RequestQueue对象。
public class Volley {
private static final String DEFAULT_CACHE_DIR = "volley";
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
private static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
// Only use HttpURLConnection
if (stack == null) {
stack = new HurlStack();
}
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
}
我们从代码可以看出最后实现的就是RequestQueue newRequestQueue(Context context, HttpStack stack) 这个方法,而stack开始是空的,然后创建了一个新的HurlStack()对象,开始还创建了一个文件作为缓存,接下来又新建了一个Network的实现类BasicNetwork对象,然后调用RequestQueue的构造方法创建了RequestQueue对象,它的两个实参就是之前的创建的对象cacheDir和network。然后调用RequestQueue的start方法,来开启缓存队列和网络请求队列.最后返回requestQueue对象。
接下来来分析RequestQueue类代码。
public void start() {
// 关闭所有正在运行的缓存线程和网络请求线程.
stop();
// 开启缓存线程.
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start(); //CacheDispatcher类继承Thread,这里调用的是父类的start.
// 默认开启DEFAULT_NETWORK_THREAD_POOL_SIZE(4)个线程来执行request网络请求.
for (int i = 0; i < mDispatchers.length; i ++) { //默认的为4
// 将NetworkDispatcher线程与mNetworkQueue这个队列进行绑定.
// NetworkDispatcher会使用生产者-消费者模型从mNetworkQueue获取request请求,并执行.
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
上面就是在Volley里面调用的RequestQueue的start方法,在这里面最主要是开启缓存线程和网络请求线程,在这里mDispatchers.length默认是4,所以在执行完start后会有5个线程,1个缓存线程和4个网络请求线程,为什么会有1个缓存线程和4个网络请求线程呢?因为却大多数请求都会经过网络请求线程进行消费。然后在逐步来看mCacheDispatcher对象,它是CacheDispatcher的一个实例,然后调用CacheDispatcher.start方法。
public CacheDispatcher(
BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,
Cache cache, ResponseDelivery delivery) {
mCacheQueue = cacheQueue;
mNetworkQueue = networkQueue;
mCache = cache;
mDelivery = delivery;
}
/** 通过标记位机制强行停止CacheDispatcher线程. */
public void quit() {
mQuit = true;
interrupt();
}
@Override
public void run() {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 初始化DiskBasedCache缓存类.
mCache.initialize();
while (true) {
try {
// 从缓存队列中获取request请求.(缓存队列实现了生产者-消费者队列模型)
final Request<?> request = mCacheQueue.take();
// 判断请求是否被取消
if (request.isCanceled()) {
request.finish("cache-discard-canceled"); //结束当前请求,并直接开始下次循环
continue;
}
// 从缓存系统中获取request请求结果Cache.Entry.
Cache.Entry entry = mCache.get(request.getCacheKey()); //Cache是缓存内存的接口,里面的内部类Entry才是真正HTTP请求缓存实体类
if (entry == null) {
// 如果缓存系统中没有该缓存请求,则将request加入到网络请求队列中.
// 由于NetworkQueue跟NetworkDispatcher线程关联,并且也是生产者-消费者队列,
// 所以这里添加request请求就相当于将request执行网络请求.
mNetworkQueue.put(request);
continue;
}
// 判断缓存结果是否过期.
if (entry.isExpired()) {
request.setCacheEntry(entry);
// 过期的缓存需要重新执行request请求.
mNetworkQueue.put(request);
continue;
}
// We have a cache hit; parse its data for delivery back to the request.
Response<?> response = request.parseNetworkResponse(new NetworkResponse(entry.data, //NetworkResponse网络请求结果抽象类
entry.responseHeaders)); //直接解析缓存里面的请求结果
// 判断Request请求结果是否新鲜?
if (!entry.refreshNeeded()) {
// 请求结果新鲜,则直接将请求结果分发,进行异步回调用户接口.
mDelivery.postResponse(request, response); //没有找到mDelivery的实现类,postResponse()具体实现逻辑未知.
} else {
// 请求结果不新鲜,但是同样还是将缓存结果返回给用户,并且同时执行网络请求,刷新Request网络结果缓存.
request.setCacheEntry(entry);
response.intermediate = true;
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
} catch (InterruptedException e) {
e.printStackTrace();
if (mQuit) {
return;
}
}
}
}
从上面代码可以看出CacheDispatcher继承Thread,是一个线程,在RequestQueue里面启动CacheDispatcher线程。接下来我们来看run方法里面。在这里面while(true)表明该线程启动后一直处理。首先从缓存队列中取出request,然后通过request.getCacheKey()来获取缓存结果,若缓存结果为空则直接把请求加入网络请求队列,然后continue。若结果不为空则判断缓存结果是否过期,若过期还是加入网络请求队列,否则就直接从缓存中获取结果,将调用request.parseNetworkResponse将结果进行解析,然后判断请求结果是否新鲜,若新鲜则直接调用ResponseDelivery.postResponse方法将解析结果进行分发到主线程,否则再分发结果后,重新发起网络请求,刷新Request网络结果缓存.。
这就是我们完全走缓存路线的全部流程代码.
接下来来分析走网络请求队列部分。
@Override
public void run() {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// 使用BlockingQueue实现了生产者-消费者模型.
// 消费者是该调度线程.
// 生产者是request网络请求.
request = mQueue.take();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
try {
if (request.isCanceled()) {
continue;
}
addTrafficStatsTag(request);
// 真正执行网络请求的地方.
NetworkResponse networkResponse = mNetwork.performRequest(request); //这里是调用Network接口的实现类BasicNetwork的performRequest(request),经过分析最终是调用调用HurlStack的performRequest方法执行网络请求
// 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;
}
// 在当前线程中解析网络结果.
// 不同的Request实现的parseNetworkResponse是不同的(例如StringRequest和JsonRequest).
Response<?> response = request.parseNetworkResponse(networkResponse); //调用具体Request的parseNetworkResponse()方法解析网络结果.
//
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
} //如果这个请求可以缓存,且response中的request缓存不为空,则把response中的request缓存加入缓存结果mCache。
// 将网络请求结果进行传递.
// ResponseDelivery调用顺序如下:
// ResponseDelivery.postResponse==>ResponseDeliveryRunnable[Runnable]->run
// ==>Request->deliverResponse==>用户设置的Listener回调接口
request.markDelivered(); //标记已经返回结果
mDelivery.postResponse(request, response); //调用request的deliverResponse()将结果传递给listener回调接口
} catch (VolleyError volleyError) {
volleyError.printStackTrace();
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
mDelivery.postError(request, volleyError);
}
}
}
同上,NetworkDispatcher也是一个线程,我主要来看它的run方法。同样使用生产者和消费者模式,调用 NetworkResponse networkResponse = mNetwork.performRequest(request)来处理请求,也就是调用BasicNetwork的performRequest方法来处理,等下我们再来看Network的实现类BasicNetwork.继续往下看,判断返回结果是否为304状态且已经返回过结果,如果是则关闭request,continue.反之,解析结果。如果该request可以缓存且返回结果不为空,则在缓存中存储结果(在这里通过
mCache.put(request.getCacheKey(), response.cacheEntry)来储存,这跟之前我们分析缓存处理中从缓存中取结果所用方法相对应mCache.get(request.getCacheKey()))。然后设置该request的标记位markDelivered为true,表明已经返回过结果。
最后调用ResponseDelivery的postResponse方法分发结果。(这里最后是调用ResponseDelivery的实现类ExecutorDelivery的重写方法postResponse来处理,在这里面在返回成功后会调用具体request里面的重写方法deliverResponse()方法。)
最后我们再来看看之前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 {
// 构造Cache的HTTP headers,主要是添加If-None-Match和If-Modified-Since两个字段
// 当客户端发送的是一个条件验证请求时,服务器可能返回304状态码.
// If-Modified-Since:代表服务器上次修改是的日期值.
// If-None-Match:服务器上次返回的ETag响应头的值.
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
// 调用HurlStack的performRequest方法执行网络请求, 并将请求结果存入httpResponse变量中
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine(); //获取返回结果的状态
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// 当服务端返回304状态码时,直接将Volley缓存中结果返回
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
Cache.Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// A HTTP 304 response dose not have all header filed. We
// have to use the header fields from the cache entry plus
// the new ones from the response.
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 mush check
if (httpResponse.getEntity() != null) {
responseContents = entityToBytes(httpResponse.getEntity());
} else {
responseContents = new byte[0];
}
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 NoConnctionError(e);
}
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) {
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?
throw new ServerError(networkResponse);
}
} else {
attemptRetryOnException("network", request, new NetworkError());
}
}
}
}
这里面最主要的方法就是performRequest()方法,在之前处理request时其实就是调用这个方法执行。
先看这句httpResponse = mHttpStack.performRequest(request, headers);它是调用HttpStack的performRequest方法来处理,所以最终的处理落在了HttpStack里面,而我们在这里面传进的是newRequestQueue方法中传入的HurlStack类。也就是通过HttpURLConnection来处理请求,接下来判断返回结果状态码是否为304,若是则直接返回缓存中的结果。反之返回将返回流数据转换成字节数组,作为返回的NetworkResponse的数据。至此网络处理请求也说完了。
接下来我们去看ExecutorDelivery源码
@SuppressWarnings("unchecked")
private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable;
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
}
@Override
public void run() {
// 如果request被取消,则不回调用户设置的Listener接口
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// 通过response状态标志,来判断是回调用户设置的Listener接口还是ErrorListener接口
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
// 通知RequestQueue终止该Request请求
mRequest.finish("done");
}
if (mRunnable != null) {
mRunnable.run();
}
}
}
在这里我们主要就看一段
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
这就是调用request中重写的方法deliverResponse和deliverError方法,将结果返回到主线程listener的onResponse()方法处理。