Volley框架的使用
Volley网络框架的使用方式绝大部分人都已经很熟悉了。
最简单的就是通过Volley提供的静态方法newRequestQueue(Context context)来返回一个消息队列MessageQueue,然后在需要使用时将构造的网络请求消息添加到队列中去,这样就可以完成网络请求
//定义全局的请求队列
requestQueue=Volley.newRequestQueue(getApplicationContext());
//实例化一个请求,并添加到请求队列中去:
String url = "";
//实例化一个新的StringRequest,参数依次为:请求方式,请求的URL,请求成功的回调接口,请求失败的回调接口。
StringRequest request = new StringRequest(Method.GET, url, new Listener<String>() {
@Override
public void onResponse(String arg0) {
Toast.makeText(MainActivity.this, arg0, Toast.LENGTH_SHORT).show();;
Log.e("successful", arg0);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError arg0) {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this, arg0.toString(), Toast.LENGTH_SHORT).show();
Log.e("failed", arg0.toString());
}
});
//为每一个请求设置Tag标记,便于后期对request的管理
request.setTag("testGet");
//添加到请求队列中去
MyApplication.getHttpQueues().add(request)
Volley的具体使用方式可以看这篇博文《Volley框架的使用》
Volley的源码(基于Vollley1.0.11)
在上边可以看到我们通过调用Volley提供的静态方法newRequestQueue来获取到一个消息队列,其源码其实是调用了另一个newRequestQueue方法,源码中有多个不同的newRequestQueue函数
//所有的newRequestQueue函数都是调用的该函数,传入参数依次为上下文对象,http请求栈,缓存的最大值
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
//用于缓存的文件路径
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) {
}
//传入的stack为空,也就是没有自定义stack
if (stack == null) {
//根据不同的SDK版本使用不同的Stack进行存储
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
//在构造好存储队列之后,构造网络请求
Network network = new BasicNetwork(stack);
//构造请求队列
RequestQueue queue;
if (maxDiskCacheBytes <= -1)
{
// No maximum size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
}
else
{
// Disk cache size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
}
//让队列开始工作
queue.start();
return queue;
}
//---下边的函数都是调用的第一个函数---
//传入两个参数
public static RequestQueue newRequestQueue(Context context, int maxDiskCacheBytes) {
return newRequestQueue(context, null, maxDiskCacheBytes);
}
//传入两个参数
public static RequestQueue newRequestQueue(Context context, HttpStack stack)
{
return newRequestQueue(context, stack, -1);
}
//只有一个传入参数
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
//
由此可以梳理一下调用newRequest()方法之后所发生的的操作。
- 首先生成了文件的缓存路径
- 然后判断是否自定义了stack队列,若没有定义队列,则根据SDK版本生成默认的stack,SDK大于9使用HurlStack,否则使用HttpClientStack
- 声明网络请求的实例
- 根据传入的maxDiskCacheBytes来构造请求队列
可以看到其工作流程很清晰,其中主要分为stack的创建,network的创建,和requestqueue的创建。
stack创建
//传入的stack为空,也就是没有自定义stack,创建默认的stack
if (stack == null) {
//根据不同的SDK版本使用不同的Stack进行存储
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
默认的stack根据SDK版本的不同分为HurlStack和HttpClientStack,两者其实都是HttpStack的实现类,主要区别在于适用的SDK版本不一样,HurlStack更加适用于Android版本在2.3和更高版本,因为其内部实现是用了 HttpURLConnection这个类。而HttpClientStack适用于Android版本在2.2和更低版本(现在市面上已经没有了)。
首先来看一下低版本中使用的HttpClientStack的实现:
HttpClientStack
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
//首先是创建http的request
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
addHeaders(httpRequest, additionalHeaders);
addHeaders(httpRequest, request.getHeaders());
onPrepareRequest(httpRequest);
//获取到request请求中的参数
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);
//将Request提交给HttpClient去执行,并将执行结果返回
return mClient.execute(httpRequest);
}
在这里边主要有三个动作:
- 创建HttpRequest
- 封装参数
- 将HttpRequest提交给HttpClient去执行(在外部是传入了一个AndroidHttpClient的实例)
其实HurlStack所做的操作和HttpClientStack类似,只是在最后执行网络请求的时候HurlStack使用的是HttpURLConnection,而HttpClientStack使用HttpClient来执行任务。接下来看一下HurlStack的具体实现:
HurlStack:
以下是HurlStack中的核心方法performRequest,实现了HttpStack接口定义的方法
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
//根据请求来获取url地址
String url = request.getUrl();
//保存请求头
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
//如果有需要可以通过UrlRewriter接口重写url地址
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
//生成最终使用的url
URL parsedUrl = new URL(url);
//开启连接
HttpURLConnection connection = openConnection(parsedUrl, request);
//吧request中请求头的信息添加进去
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
//设置请求的参数
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);
//从connecttion中取出实体并保存在response中
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);
}
}
//返回响应response
return response;
}
以上这两种方式就是Volley中两种不同的请求队列的方式,在构造完stack之后就需要用构造好的stack来创建一个Netwoork实例。
构造网络请求
Network network = new BasicNetwork(stack);
在下边这个方法中完成了对网络请求的发起和对返回的response的校验封装,最终返回一个NetworkResponse 实例
@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>();
//添加缓存头,从请求中获取需要缓存的字段
addCacheHeaders(headers, request.getCacheEntry());
//调用HttpStack定义的方法来执行请求,具体使用哪种stack由当前SDK版本决定
httpResponse = mHttpStack.performRequest(request, headers);
//从之前返回的httpResponse中获取返回信息
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.处理缓存的更新
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// 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);
}
// Handle moved resources重定向url进行请求
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
String newUrl = responseHeaders.get("Location");
request.setRedirectUrl(newUrl);
}
// Some responses such as 204s do not have content. We must check.
//检查返回的response是否有内容,有的话取出保存,没有的话用byte数组填充。
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;
//用llog输出这是一次缓慢的网络请求
logSlowRequests(requestLifetime, request, responseContents, statusLine);
//如果返回的状态码小于200或者是大于299,报出异常
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 = 0;
NetworkResponse networkResponse = null;
if (httpResponse != null) {
statusCode = httpResponse.getStatusLine().getStatusCode();
} else {
throw new NoConnectionError(e);
}
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
VolleyLog.e("Request at %s has been redirected to %s", request.getOriginUrl(), request.getUrl());
} else {
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
}
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 == HttpStatus.SC_MOVED_PERMANENTLY ||
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
attemptRetryOnException("redirect",
request, new AuthFailureError(networkResponse));
} else {
// TODO: Only throw ServerError for 5xx status codes.
throw new ServerError(networkResponse);
}
} else {
throw new NetworkError(networkResponse);
}
}
}
}
这样就完成了一个Network的工作,实际上的网络请求都是由这一部分来完成。
构造requestQueue
RequestQueue queue;
if (maxDiskCacheBytes <= -1)
{
// No maximum size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
}
else
{
// Disk cache size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
}
queue.start();
在创建好实际的网络请求类后创建请求队列,并将网络请求类的实例传入,这样在queue开始循环之后就可以不断的进行网络请求的操作。上边的代码显示核心的方法为RequestQueue的构造方法和他的start方法。
/**
* @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
* 可以看到主要是该构造函数的使用,其中有用于缓存的cache,用于执行任务的dispatcher,用于定义了网络请求的network,用于返回response和error的delivery
*
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
/**
* 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
*/
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);
}
在构造好RequestQueue之后就可以通过start方法来开始网络请求,其实主要是开启了NetWorkDispatcher的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();
}
}
在上边中的NetworkDispatcher其实是一个继承自Thread类的工作线程,他通过在run方法中进行不断循环来不停的执行任务,通过start方法来开启线程。下边是其核心的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");
//检查Request是否已经被取消
// If the request was cancelled already, do not perform the
// network request.
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
//添加tag标记
addTrafficStatsTag(request);
//真正的通过NetWork实例来执行网络请求
// Perform the network request.
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.
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
//将response进行拼接组装
// Parse the response here on the worker thread.
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
//进行cache的更新
// Write to cache if applicable.
// TODO: Only update cache metadata instead of entire record for 304s.
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);
} catch (VolleyError volleyError) {
//接下来就是异常的捕获和处理
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);
}
}
}
到此为止Volley已经完成了任务队列的初始化和创建,也完成了从任务被提交到任务队列中,到被工作线程取出执行,到结果的返回和拼装,以及最后response的分发的整个流程,我们可以来梳理一下整个过程。
这就是整个的请求流程的源码,接下来重点就在于对各类型的Request和Response的解读上。