现在这个框架已经被用得很少了,但是目前我们项目中还有用到,那就尝试分析源码吧。
源码地址:https://github.com/google/volley
一.源码解析步骤:
1. volley类:
public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {
BasicNetwork network;
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
/***
* 在9及以上版本,采用HurlStack ,这个类中封装了用HttpURLConnection 处理网络请求返回HttpResponse
*
***/
network = new BasicNetwork(new HurlStack());
} else {
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info =
context.getPackageManager().getPackageInfo(packageName, /* flags= */ 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
//在9以下的版本,采用HttpClientStack,里面封装了HttpClient处理网络请求返回HttpResponse
network =
new BasicNetwork(
new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));
}
} else { //这里使用户自定义业务处理网络请求返回HttpResponse
network = new BasicNetwork(stack);
}
//创建好缓存目录,将上述处理HttpResponse 的network封装到RequestQueue中,调用queue中的start方法,开始分发这个任务
return newRequestQueue(context, network);
}
2. RequestQueue类:
这个类中封装了一个线程池来调度和派发网络请求任务,默认线程的数量是4。主要是看里面的start方法。
/** Starts the dispatchers in this queue. */
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.
for (int i = 0; i < mDispatchers.length; i++) {//根据线程池中线程的数量,创建网络请求线程任务
NetworkDispatcher networkDispatcher =
new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
3. NetworkDispatcher类
这个类继承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");
}
}
}
// 处理网络请求的逻辑
@VisibleForTesting
void processRequest(Request<?> request) {
long startTimeMs = SystemClock.elapsedRealtime();
try {
request.addMarker("network-queue-take");//给当前线程加上标记,主要是为了调试用的
// If the request was cancelled already, do not perform the
// network request.
if (request.isCanceled()) {
request.finish("network-discard-cancelled");//从请求队列中去掉此次请求标记
request.notifyListenerResponseNotUsable();
return;
}
addTrafficStatsTag(request);
// Perform the network request.处理网络请求,返回NetworkResponse
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.
//如果服务端返回304并且已经将结果返回分发出去了,就标记此次请求已经完成
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
request.notifyListenerResponseNotUsable();
return;
}
// Parse the response here on the worker thread.
//抽象方法,将请求返回封装成泛型response,由调用者自己实现
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");//线程标记
//如果请求头需要缓存的话,需要将key和entry保存在缓存中
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);//将请求和响应分发出去,包括正确的和错误的响应结果
request.notifyListenerResponseReceived(response);//用listener回调请求结果
} 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();
}
}
4.CacheDispatcher类:
这也是继承Thread ,查看其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 {
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 CacheDispatcher thread; "
+ "use quit() to terminate it");
}
}
}
//从缓存队列中取出一个请求开始处理
@VisibleForTesting
void processRequest(final Request<?> request) throws InterruptedException {
request.addMarker("cache-queue-take");//线程标记,调试用途
// If the request has been canceled, don't bother dispatching it.
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
return;
}
// 根据key值获取存储的entry
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {//如果为空,说明缓存内容已经丢失了
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {//去除重复请求
mNetworkQueue.put(request);//添加到网络请求队列中
}
return;
}
// If it is completely expired, just send it to the network.
if (entry.isExpired()) {//如果有缓存,但是已经过期了
request.addMarker("cache-hit-expired");//线程标记
request.setCacheEntry(entry);
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {//去除重复请求
mNetworkQueue.put(request);//添加到网络请求队列中
}
return;
}
// We have a cache hit; parse its data for delivery back to the request.
request.addMarker("cache-hit");//缓存有效
//抽象方法,将相应的信息封装成泛型response
Response<?> response =
request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
// 没有完全过期的缓存,分发出去
mDelivery.postResponse(request, response);
} else {
// Soft-expired cache hit. We can deliver the cached response,
// but we need to also send the request to the network for
// refreshing.
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);
}
}
}
5.StringRequest类
从上面我们分析中可以看到一个抽象方法:
Response<?> response = request.parseNetworkResponse(networkResponse)
调用者可以采用StringRequest来解析Response
@Override
@SuppressWarnings("DefaultCharset")
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
//根据responseData和响应头中的字符集,创建String类型
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
// Since minSdkVersion = 8, we can't call
// new String(response.data, Charset.defaultCharset())
// So suppress the warning instead.
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
6.Volley为什么不能处理大批量数据?
在AdaptedHttpStack 中,在处理返回HttpResponse的时候,有这样一行抛出异常:
@Override
public HttpResponse executeRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
org.apache.http.HttpResponse apacheResp;
try {
apacheResp = mHttpStack.performRequest(request, additionalHeaders);
} catch (ConnectTimeoutException e) {
// BasicNetwork won't know that this exception should be retried like a timeout, since
// it's an Apache-specific error, so wrap it in a standard timeout exception.
throw new SocketTimeoutException(e.getMessage());
}
int statusCode = apacheResp.getStatusLine().getStatusCode();
org.apache.http.Header[] headers = apacheResp.getAllHeaders();
List<Header> headerList = new ArrayList<>(headers.length);
for (org.apache.http.Header header : headers) {
headerList.add(new Header(header.getName(), header.getValue()));
}
if (apacheResp.getEntity() == null) {
return new HttpResponse(statusCode, headerList);
}
//服务端返回long类型的数据,将它强转为int类型的时候,如果数据量过大,int和long类型肯定不相等,这个时候就抛出异常,提示数据量太大了。
long contentLength = apacheResp.getEntity().getContentLength();
if ((int) contentLength != contentLength) {
throw new IOException("Response too large: " + contentLength);
}
return new HttpResponse(
statusCode,
headerList,
(int) apacheResp.getEntity().getContentLength(),
apacheResp.getEntity().getContent());
}