volley是google提供的一个网络通信框架,非常适合我们进行一些数据量不大、频繁的网络通信操作。这是一个开源项目,地址为https://github.com/google/volley,今天我们就分章节来分析一下volley框架源码,探寻它内部的实现机制。
这里列举一个volley最基本的用法,通过该用法入手,来一步步分析volley是怎么进行网络通信的。
RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());
StringRequest stringRequest_get = new StringRequest("https://www.baidu.com",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("TAG", response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage(), error);
}
});
mQueue.add(stringRequest);
通过上面的代码我们看到,要想使用volley首先要通过Volley.newRequestQueue(context)来生成一个RequestQueue,RequestQueue可以理解为请求队列,里面包含了缓存调度线程、网络请求调度线程等,后面我们会详细分析。
public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {
BasicNetwork network;
//根据android版本,请求网络使用不同的工具类,httpclient或HttpURLConnection
// HurlStack内部封装的是HttpURLConnection
// HttpClientStack内部封装的是HttpClient
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
network = new BasicNetwork(new HurlStack());
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
// At some point in the future we'll move our minSdkVersion past Froyo and can
// delete this fallback (along with all Apache HTTP code).
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info =
context.getPackageManager().getPackageInfo(packageName, /* flags= */ 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
network =
new BasicNetwork(
new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));
}
} else {
network = new BasicNetwork(stack);
}
return newRequestQueue(context, network);
}
最后return的是newRequestQueue(context, network)。
private static final String DEFAULT_CACHE_DIR = "volley";
private static RequestQueue newRequestQueue(Context context, Network network) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
cacheDir为缓存目录,默认为工程缓存目录拼接上volley。
queue.start()是开启了上文提到的缓存线程和网络请求线程,以便他们可以处理我们发出的网络请求。
public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
// 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.
//开启网络请求线程,默认开启4个线程来执行网络请求
for (int i = 0; i < mDispatchers.length; i++) {
//这里,NetworkDispatcher和mNetworkQueue会关联起来,NetworkDispatcher会从networkqueue中取出request请求网路
NetworkDispatcher networkDispatcher =
new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
默认会开启1个缓存线程和4个网络线程。
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
生成RequestQueue之后,需要将我们的request添加进去,调用的是RequestQueue.add()
public Request add(Request 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.
//如果request不允许缓存,则直接加入到网路请求队列中 默认是可以缓存
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
mCacheQueue.add(request);
return request;
}
上面方法中的mNetworkQueue是网络请求队列,mCacheQueue是缓存请求队列。
可以看到,如果这个请求不可以被缓存,那么会直接将请求添加进mNetworkQueue,由网络请求调度器来处理;若request可以缓存,则添加进mCacheQueue,由缓存调度器来处理。request请求默认都是可以缓存的,可以通过调用request.setShouldCache(boolean)来设置一个request是否可以缓存。
/**
* Set whether or not responses to this request should be cached.
*
* @return This Request object to allow for chaining.
*/
public final Request> setShouldCache(boolean shouldCache) {
mShouldCache = shouldCache;
return this;
}
上文提到,不需要缓存的网络请求会直接添加到mNetworkQueue中,mNetworkQueue是一个具备优先级的请求队列,它的数据结构如下:
//网络请求的队列
private final PriorityBlockingQueue >> mNetworkQueue = new PriorityBlockingQueue<>();
mNetworkQueue在queue.start()中创建NetworkDispatcher时传给了NetworkDispatcher。
NetworkDispatcher networkDispatcher =
new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
NetworkDispatcher本质就是一个thread。
public class NetworkDispatcher extends Thread {
...
}
我们看下它的run方法:
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
try {
processRequest();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
Thread.currentThread().interrupt();
return;
}
VolleyLog.e(
"Ignoring spurious interrupt of NetworkDispatcher thread; "
+ "use quit() to terminate it");
}
}
很简单,设置当前线程为后台线程之后,在一个死循环中执行了processRequest(),那我们继续往下看
private void processRequest() throws InterruptedException {
// Take a request from the queue.
Request> request = mQueue.take();
processRequest(request);
}
这里的mQueue就是通过构造方法传进来的mNetworkQueue,里面有我们添加进去的request,当mQueue.take()取到request取到request时会processRequest(request),否则会在take()方法一直等待(这里可以自行查看PriorityBlockingQueue源码),这里可能会有同学有疑问,四个NetworkDispatcher会不会同时抢到同一个request然后发出请求,这里就得再次说下take()的源码了,take()通过ReentrantLock+Condition实现了线程安全
private final Condition notEmpty;
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
E result;
try {
while ( (result = dequeue()) == null)
notEmpty.await();
} finally {
lock.unlock();
}
return result;
}
put()的时候,会调用notEmpty.signal()唤醒。
执行request请求走的是processRequest方法,
@VisibleForTesting
void processRequest(Request> request) {
long startTimeMs = SystemClock.elapsedRealtime();
try {
request.addMarker("network-queue-take");
// 判断这个请求有没有取消,如果已经取消,则不发出改网路请求
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
request.notifyListenerResponseNotUsable();
return;
}
addTrafficStatsTag(request);
//真正执行网络请求的地方
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
request.notifyListenerResponseNotUsable();
return;
}
// 在当前线程中解析网络结果.
// 不同的Request实现的parseNetworkResponse是不同的(例如StringRequest和JsonRequest).
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");
}
// 将网络请求结果进行传递.
// ResponseDelivery调用顺序如下:
// ResponseDelivery.postResponse==>ResponseDeliveryRunnable[Runnable]->run
// ==>Request->deliverResponse==>用户设置的Listener回调接口
// Post the response back.
request.markDelivered();
mDelivery.postResponse(request, response);
request.notifyListenerResponseReceived(response);
} catch (VolleyError volleyError) {
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
request.notifyListenerResponseNotUsable();
} 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);
request.notifyListenerResponseNotUsable();
}
}
调用到了network的performRequest,我们来看一下
@Override
public NetworkResponse performRequest(Request> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
List responseHeaders = Collections.emptyList();
try {
// Gather headers.
// 构造Cache的HTTP headers,主要是添加If-None-Match和If-Modified-Since两个字段
// 当客户端发送的是一个条件验证请求时,服务器可能返回304状态码.
// 304表示未修改,客户端缓存是最新的,客户端应该继续使用它
// If-Modified-Since:代表服务器上次修改是的日期值.
// If-None-Match:服务器上次返回的ETag响应头的值.
//这里 HTTP_NOT_MODIFIED ==304
Map additionalRequestHeaders =
getCacheHeaders(request.getCacheEntry());
//调用basehttpstack的executeRequest方法执行网络请求,并将结果返回
httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeade
int statusCode = httpResponse.getStatusCode();
responseHeaders = httpResponse.getHeaders();
// Handle cache validation.
//分析返回结果 判断状态码
if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
//状态码是304,直接讲缓存中的结果返回
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(
HttpURLConnection.HTTP_NOT_MODIFIED,
/* data= */ null,
/* notModified= */ true,
SystemClock.elapsedRealtime() - requestStart,
responseHeaders);
}
// Combine cached and response headers so the response will be complete.
List combinedHeaders = combineHeaders(responseHeaders, entry);
return new NetworkResponse(
HttpURLConnection.HTTP_NOT_MODIFIED,
entry.data,
/* notModified= */ true,
SystemClock.elapsedRealtime() - requestStart,
combinedHeaders);
}
// Some responses such as 204s do not have content. We must check.
InputStream inputStream = httpResponse.getContent();
if (inputStream != null) {
responseContents =
inputStreamToBytes(inputStream, httpResponse.getContentLength())
} 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, statusCode);
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
return new NetworkResponse(
statusCode,
responseContents,
/* notModified= */ false,
SystemClock.elapsedRealtime() - requestStart,
responseHeaders);
} catch (SocketTimeoutException e) {
//重试操作
attemptRetryOnException("socket", request, new TimeoutError());
} catch (MalformedURLException e) {
...
}
CacheDispatcher请求的流程跟NetworkDispatcher大致相同,只是在发出网络请求的时候做了对缓存的判断和处理。
@VisibleForTesting
void processRequest(final Request> request) throws InterruptedException {
request.addMarker("cache-queue-take");
//判断请求是否被取消
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
return;
}
// 从缓存系统中获取request请求结果Cache.Entry.
// Attempt to retrieve this item from cache.
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
// 如果缓存系统中没有该缓存请求,则将request加入到网络请求队列中.
// 由于NetworkQueue跟NetworkDispatcher线程关联,并且也是生产者-消费者队列,
// 所以这里添加request请求就相当于将request执行网络请求.
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
mNetworkQueue.put(request);
}
return;
}
//判断缓存是否过期,过期需要重新执行网络请求
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
mNetworkQueue.put(request);
}
return;
}
request.addMarker("cache-hit");
Response> response =
request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
//判断request请求结果是否新鲜????
//判断缓存结果是否需要刷新
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;
//需要刷新时,将结果返回,同时执行网络请求更新缓存
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
// Post the intermediate response back to the user and have
// the delivery then forward the request along to the network.
mDelivery.postResponse(
request,
response,
new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Restore the interrupted status
Thread.currentThread().interrupt();
}
}
});
} else {
// request has been added to list of waiting requests
// to receive the network response from the first request once it returns.
mDelivery.postResponse(request, response);
}
}
}
请求结果回调主要涉及Response(子类有NetworkResponse)和ResponseDelivery(ResponseDelivery是一个接口,实现接口的类有ExecutorDelivery,ImmediateResponseDelivery继承自ExecutorDelivery)
我们重点看一下ExecutorDelivery
public ExecutorDelivery(final Handler handler) {
//观察构造方法调用的地方,handler构造的时候传的mainlooper
// 所有的Runnable通过绑定主线程Looper的Handler对象最终在主线程执行.
mResponsePoster =
new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
通过前面的分析我们知道,请求网络之后,如果是正常返回,则调用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));
}
可见真正执行走的是ResponseDeliveryRunnable的run方法
@SuppressWarnings("unchecked")
@Override
public void run() {
// NOTE: If cancel() is called off the thread that we're currently running in (by
// default, the main thread), we cannot guarantee that deliverResponse()/deliverError()
// won't be called, since it may be canceled after we check isCanceled() but before we
// deliver the response. Apps concerned about this guarantee must either call cancel()
// from the same thread or implement their own guarantee about not invoking their
// listener after cancel() has been called.
// 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();
}
}
这个方法也很简单,首先做了request是否cancel的判断,然后根据response是否成功,来决定调用request的deliverResponse还是deliverError。
request的deliverResponse是抽象方法,需要的request实现类来具体实现。
/**
* Subclasses must implement this to perform delivery of the parsed response to their listeners.
* The given response is guaranteed to be non-null; responses that fail to parse are not
* delivered.
*
* @param response The parsed response returned by {@link
* #parseNetworkResponse(NetworkResponse)}
*/
protected abstract void deliverResponse(T response);
以StringRequest为例来看
@Override
protected void deliverResponse(String response) {
Response.Listener listener;
synchronized (mLock) {
listener = mListener;
}
if (listener != null) {
listener.onResponse(response);
}
}
发现是通过listener接口进一步回调的,而listener接口是在构造方法中传进来的,一同传进来的还有errorlistener,即在请求出错时回调用的,error回调流程跟正常流程基本一致。
public StringRequest(
int method,
String url,
Listener<String> listener,
@Nullable ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
这里也就对应到了我们文章开头举的例子,发出请求时,在构造方法中写好listener,在网络请求完成之后就可收到响应的回调了。