Android网络通信Volley框架源代码浅析(一)

时间:2021-05-11 08:02:15

尊重原创http://blog.csdn.net/yuanzeyao/article/details/25837897

从今天開始,我打算为大家呈现关于Volley框架的源代码分析的文章,Volley框架是Google在2013年公布的。主要用于实现频繁并且粒度比較细小的Http请求。在此之前Android中进行Http请求一般是使用HttpUrlConnection和HttpClient进行,可是使用起来非常麻烦,并且效率比較地下,我想谷歌正式基于此种原因公布了Volley框架。事实上出了Volley框架意外,也有一些http请求开源项目。比方使用比較广泛的有async-http,UniversImageLoader等等,当中async-http主要用来实现异步http请求,而后者主要用来请求图片。Volley具有以上两种框架的功能,并且是Google公司公布。我想作为Android开发人员,非常有必要研究一下该框架。

1、下载Volley框架
git clone https://android.googlesource.com/platform/frameworks/volley

2、引用Volley框架
引用该框架的方式主要有两种:
(1):直接导入Volley框架,作为lib使用
(2):编译Volley成jar包

3、Volley的功能:
前面已经提及了Volley是一个用于http请求的框架,其主要功能例如以下:
json,xml,String,Image等资源的请求,当然我们还能够依据自己的须要来改写Volley框架源代码。从而实现自己的功能

4、Volley源代码分析

温馨提醒:假设是第一次看Volley源代码,第一遍没看懂没关系,将源代码copy下来,跟着我的思路慢慢分析,将文章从头到后多看几遍就ok了。由于Volley的一些关键类都互对应用。我仅仅能一个一个的分析了,等你看完我全部的文章,然后再从头看一变。相信你定有所收获

当然阅读次文章之前。最好学会知道Volley框架的基本使用,由于网络上非常多相似的教程。我在此处就不再描写叙述了,后期假设有时间我也会解说一下Volley的应用
我们就从Volley这个类開始吧

(1) Volley.java

public class Volley {

    //缓存文件夹
private static final String DEFAULT_CACHE_DIR = "volley";
//创建一个默认的请求队列。我们的请求创建好后。放入该队列就可以
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
//缓存文件夹
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,那么自己创建一个,假设sdk>9(就是2.3以上)。那么使用
HttpURLConnection实现http请求,假设2.3曾经使用HttpClient实现,由于在2.3曾经httpURLConnection不稳定
*/
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
//Network类是一个网络请求类,使用stack进行网络请求
Network network = new BasicNetwork(stack);
//真正创建一个请求队列,传入一个磁盘缓存和网络请求类
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
//启动请求队列。事实上里面就是启动了一些线程。不断监听是否有请求
queue.start(); return queue;
} /**
* 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.
* @return A started {@link RequestQueue} instance.
*/
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
}

Volley类主要用来创建一个请求队列,我们的不论什么请求(请求字符串,json,xml)都放入到这个队列中(事实上里面有两个队列,后面我们慢慢学习,临时简单理解为一个)。

创建完队列后。调用start方法,就会启动一些线程(临时不关注多少条线程),不断监听队里里面是否有请求,假设有请求则运行http请求,在2.3之前的版本号中。Http请求是通过httpClient实现。在2.3以后的版本号中是通过HttpURLConnection实现,由于在2.3之前的版本号中HttpRlConnection非常不稳定

(2) HttpStack.java
以下看看HttpStack是何方神圣

public interface HttpStack {
/**
名字挺吓人的,呵呵,事实上就是一个接口,它有两个实现。各自是HurlStack,HttpClientStack,通过名字大家
能够猜出来一个基于HttpClient,一个基于HttpURLConnection
* @return the HTTP response
*/
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError; }

直接查看它的子类方法吧
首先看  HurlStack.java类
这个类是基于HttpURLConnection实现的
由于这个类比較长,我就重点解说一下

(3) HurlStack.java

/**
继承自HttpStack,我们临时就把Request抽象成一个请求。包括url,method等信息,后期我们会重点分析这个类
第二个參数就是一些请求头信息
*/
@Override
public HttpResponse performRequest(Request<? > request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
//此处一般为空,我们直接忽略掉
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);
//增加请求头
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
//这种方法名字非常长。事实上功能非常easy,就是为connection设置请求方法 如get post等等
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response.setEntity(entityFromConnection(connection));
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
//http的返回结果
return response;
}

(4) HttpClientStack.java

/**
相对照上一个方法简单。相信使用过httpClient的同学一看就明确
*/
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
//这种方法见名知意。就是创建一个HttpGet或者HttpPost
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
//增加头信息
addHeaders(httpRequest, additionalHeaders);
addHeaders(httpRequest, request.getHeaders());
//在请求之前进行准备工作,事实上是个空方法。非常想AsyncTask的onPreExecute
onPrepareRequest(httpRequest);
HttpParams httpParams = httpRequest.getParams();
int timeoutMs = request.getTimeoutMs();
// TODO: Reevaluate this connection timeout based on more wide-scale
// data collection and possibly different for wifi vs. 3G.
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
//运行请求并返回结果
return mClient.execute(httpRequest);
}

看到这里大家肯定认为这个框架也没有什么了不起嘛,和使用HttpURLConnection和HttpClient差点儿相同嘛。假设你真的这样认为那么你就大错特错了,事实上这个框架的核心在于线程的调度和缓存上面,后期我们会介绍的

回到Volley类。我们看下一个陌生的类就是Network,事实上Network只是是个接口而已,它的实现类是BaskNetwork

(5) BaskicNetwork.java

从名字我们就能够看出来。这个类就是进行网络请求的。事实上他就是对HttpurlStack或者HttpClientStack的一个封装,真正实现请求的还是上面两个类。
最核心的方法:

@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = new HashMap<String, String>();
try {
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
//调用mHttpStack运行http请求
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) {
//将返回结果封装成一个NetworkResponse
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
request.getCacheEntry() == null ? null : request.getCacheEntry().data,
responseHeaders, true);
} // 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);
} 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 = 0;
NetworkResponse networkResponse = null;
if (httpResponse != null) {
statusCode = httpResponse.getStatusLine().getStatusCode();
} else {
throw new NoConnectionError(e);
}
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
if (responseContents != null) {
networkResponse = new NetworkResponse(statusCode, responseContents,
responseHeaders, false);
if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
statusCode == HttpStatus.SC_FORBIDDEN) {
attemptRetryOnException("auth",
request, new AuthFailureError(networkResponse));
} else {
// TODO: Only throw ServerError for 5xx status codes.
throw new ServerError(networkResponse);
}
} else {
throw new NetworkError(networkResponse);
}
}
}
}

这种方法调用了mHttpStack的同名方法,仅仅只是在mHttpStack中返回的是HttpResponse。在这里返回的是NetworkResponse。

然后再看看本篇文章的最后一个类:
RequestQueue.java
我保留了一些keyword段,删除不影响理解的字段

public class RequestQueue {

	...

    //本地缓存队列。假设一个请求能够缓存,那么先放到这个队列中,假设本地缓存没有命中,则增加网络队列。见后面
private final PriorityBlockingQueue<Request<? >> mCacheQueue =
new PriorityBlockingQueue<Request<?>>(); //网络请求队列
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<? >>(); //默认的网络请求线程个数 默认四个,这个我们能够修改
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4; //本地缓存的接口
private final Cache mCache; //这个类相信大家并不陌生
private final Network mNetwork; //由于网络请求在子线程中运行,这个对象将请求结果发送到ui线程,功能非常像Handler
private final ResponseDelivery mDelivery; //网络线程数组
private NetworkDispatcher[] mDispatchers; //本地线程数组 仅仅有一条
private CacheDispatcher mCacheDispatcher; /**
创建一个请求队列
參数1:本地缓存
參数2: network 进行网络进行的包装类
參数3:网络请求线程池大小
參数4:就是一个将子线程的数据发送到ui线程的功能类,先能够不用关心
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
} public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
} /**
* 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
*/
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
} //启动本地和网络线程
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();
}
} //关闭本地和网络线程
public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatchers[i] != null) {
mDispatchers[i].quit();
}
}
} //相当于一个过滤器,对于apply方法返回true的Request能够从Queue中删除
public interface RequestFilter {
public boolean apply(Request<?> request);
} //借助上面的方法实现对没有运行的Request进行删除
public void cancelAll(RequestFilter filter) {
synchronized (mCurrentRequests) {
for (Request<?> request : mCurrentRequests) {
if (filter.apply(request)) {
request.cancel();
}
}
}
} //取消全部的请求
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;
}
});
} /**
将Request放入两个队列中的一个
*/
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"); // 假设该Request不能缓存,那么直接放入网络队列
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
} // 把Request放入具有同样CacheKey的链表中,假设没有同样的CacheKey的Request请求存在,则放入本地队列
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
// 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 {
// Insert 'null' queue for this cacheKey, indicating there is now a request in
// flight.
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
} /**
* 结束一个Request
*/
void finish(Request<?> request) {
// Remove from the set of requests currently being processed.
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
} if (request.shouldCache()) {
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
if (waitingRequests != null) {
if (VolleyLog.DEBUG) {
VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
waitingRequests.size(), cacheKey);
}
// Process all queued up requests. They won't be considered as in flight, but
// that's not a problem as the cache has been primed by 'request'.
mCacheQueue.addAll(waitingRequests);
}
}
}
}
}

写到这里先高一段落吧,来个小小的总结:Volley中有一个RequestQueue(包括本地队列和网络队列),就是请求队列。每个http请求都被封装成了一个Request,通过队列的add方法增加队列,假设一个Request能够缓存,那么先增加本地队列。假设不能缓存则增加网络队列

欢迎继续阅读Volley框架浅析(二)

欢迎留言讨论。。。