标签:
概念:
Google在I/O 2013大会上发布了Volley。它是Android平台上的网络通信库,能使网络通信更快,更简单,更健壮。
1、优点就是特别适合数据量不大但是通信频繁的场景
2、缺点就是大数据传输表现的很糟糕。
官方开源地址:
Volley Android Developer文档:http://developer.android.com/training/volley/index.html
Volley主页:https://android.googlesource.com/platform/frameworks/volley
Volley仓库:Git clone https://android.googlesource.com/platform/frameworks/volley
Volley GitHub Demo:在GitHub主页搜索Volley会有很多.
一图胜千言(来源于Volley Android Developer文档):
下面 let me 娓娓道来:
1、首先我们来看下Volley.java类:
public class Volley {
/** Default on-disk cache directory. */
private static final String DEFAULT_CACHE_DIR = "volley";
/**
* 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.
* @param stack An {@link HttpStack} to use for the network, or null for default.
* @return A started {@link RequestQueue} instance.
*/
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) {
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 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的第一步首先是通过Volley.newRequestQueue(context)得到RequestQueue队列,
这个方法默认调用了第二个重载它的方法;并且HttpStack 参数默认传null,也就是说if (stack == null)
默认会执行,
然后就判断android的版本号:
api>=9就使用HurlStack(也就是HttpURLConnection);
api<9就使用HurlStack(也就是HttpClient );
也就验证了下面这句话:
默认 Android2.3 及以上基于 HttpURLConnection,2.3 以下基于 HttpClient 实现!
究其原因,是在 Froyo(2.2) 之前,HttpURLConnection 有个重大 Bug,调用 close() 函数会影响连接池,导致连接复用失效。
接着,我们看第29行,HttpStack创建完成之后创建了Network实例。BasicNetwork是Network接口的实现,他们都在toolbox中,其作用是根据传入的HttpStack对象来处理网络请求。
2、我们先看下NetWork接口的代码:
/**
* 用于执行请求的接口。
*/
public interface Network {
/**
* Performs the specified request.
* @param request Request to process
* @return A {@link NetworkResponse} with data and caching metadata; will never be null
* @throws VolleyError on errors
*/
public NetworkResponse performRequest(Request<?> request) throws VolleyError;
}
它的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 {
// 添加请求的消息头。
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// 处理缓存验证。
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);
}
//一个HTTP304响应不具有所有消息头,我们必须从新的响应中使用高速缓存条目使用的消息头。
entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// 有一些响应如 204S 没有内容,我们必须检查。
if (httpResponse.getEntity() != null) {
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// 如果没有内容,我们就添加0字节来替换null.
responseContents = new byte[0];
}
// 如果响应特别慢,我们会LOG出来。
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,
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 NoConnectionError(e);
}
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
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) {
// Don‘t retry other client errors.
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? No reason to retry.
throw new ServerError(networkResponse);
}
} else {
attemptRetryOnException("network", request, new NetworkError());
}
}
}
}
这个方法是网络请求的具体实现,其中**第12行**mHttpStack.performRequest(request, headers);代码中的mHttpStack是Volley的newRequestQueue()方法中创建的实例。创建完实例后,紧接着new出一个RequestQueue对象,并调用它的start()方法进行启动。
3、下面我们看下它的start方法:
//在启动此队列调度。
public void start() {
stop(); // 确保任何正在运行的调度程序停止。
// 创建缓存调度并启动它。
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// 循环创建网络调度(以及相应的线程)。
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
这里的CacheDispatcher和NetworkDispatcher都是继承自Thread的,而默认情况下for循环会执行(DEFAULT_NETWORK_THREAD_POOL_SIZE)四次,也就是说当调用了Volley.newRequestQueue(context)之后,就会有五个线程一直在后台运行,不断等待网络请求的到来,其中一个CacheDispatcher是缓存线程,四个NetworkDispatcher是网络请求线程。
4、所以先看下CacheDispatcher的run方法:
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
//设置线程的优先级,默认为10.
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 使阻塞调用初始化缓存。
mCache.initialize();
while (true) {
try {
//获得从缓存队列中分流的请求,直到阻塞
//至少有一个是可用的。
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// 如果这个请求已经被取消了,不打扰它调度。
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// 尝试从缓存中获取此条目。
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// 缓存条目为空;派发到网络调度。
mNetworkQueue.put(request);
continue;
}
// 如果缓存条目过期了,将其发送到网络调度。
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
// 有缓存条目,则解析其数据递送回请求。
request.addMarker("cache-hit");
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
// 完全未过期的缓存条目,传送至响应结果中。
mDelivery.postResponse(request, response);
} else {
// 软过期的缓存条目。我们可以提供缓存的响应,
//但是我们还需要将请求发送到网络去更新。
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// 标记反应中间体。
response.intermediate = true;
// post在中间的响应返回给用户并
//递送然后沿向网络转发请求。
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
}
}
});
}
} catch (InterruptedException e) {
// 我们可能被中断,因为是时候退出了。
if (mQuit) {
return;
}
continue;
}
}
}
这里,我们看到它首先拿到一个缓存条目,判断它是否为null和是否过期来决定是否使用缓存条目。有缓存条目,则解析其数据递送回请求;否则就发送到网络调度。最后执行了mDelivery.postResponse(request, response);
5、同理,我们来分析一下NetworkDispatcher的run方法:
@Override
public void run() {
//设置线程的优先级,默认为10.
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// 直接拿从队列中的请求。
request = mQueue.take();
} catch (InterruptedException e) {
// 我们可能被中断,因而退出了。
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
// 如果请求已经取消,则不执行网络请求了。
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
// 执行网络请求。
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 如果服务器返回304,而且我们已经发表了回应,
// 我们就大功告成了(不提供第二个相同的反应)。
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// 这里解析工作线程的响应。
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// 写缓存(如果适用)。
// TODO:只有更新缓存元数据,而不是整个记录304S。
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// 发布响应返回。
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);
}
}
}
我们看到,网络调度很直接了断的执行网络请求,并处理响应; 最终也把响应的情况交给了它: mDelivery.postResponse(request, response);
6、所以接下来我们统一看一下ExecutorDelivery(ResponseDelivery接口的实现类)的postResponse():
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
这里可以看见在mResponsePoster的execute()方法中传入了一个ResponseDeliveryRunnable对象,就可以保证该对象中的run()方法就是在主线程当中运行的了,我们看下run()方法中的代码是什么样的:
@Override
public void run() {
// 如果该请求被取消,完成它,但也不对外回调提供。
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// 这取决于,提供正常响应或错误。
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
//如果这是一个中间响应,添加标记,否则,我们就大功告成了,请求就可以完成。
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
// 如果我们已经提供了一个子线程运行的,运行它。
if (mRunnable != null) {
mRunnable.run();
}
}
这段代码是最核心的,明显可以看到通过mRequest的deliverResponse或者deliverError将反馈发送到回调到UI线程。
这也是你重写实现的接口方法。
总结:
1、当一个RequestQueue被成功申请后会开启一个CacheDispatcher和4个默认的NetworkDispatcher。
2、CacheDispatcher缓存调度器最为第一层缓冲,开始工作后阻塞的从缓存序列mCacheQueue中取得请求;新的,过期的,或者 需要更新缓存请求,交给N个NetworkDispatcher进行处理;然后将应答交由Delivery分发者进行处理。
3、网络请求调度器NetworkDispatcher作为网络请求真实发生的地方,对消息交给BasicNetwork进行处理,同样的,将应答交由Delivery分发者进行处理。
4、Delivery根据请求所获得的应答情况做不同处理;若应答成功,则触发deliverResponse方法,最终会触发开发者为Request设定的Listener;若应答失败,则触发deliverError方法,最终会触发开发者为Request设定的ErrorListener。处理完后,一个Request的生命周期就结束了,Delivery会调用Request的finish操作,将其从mRequestQueue中移除,与此同时,如果等待列表中存在相同URL的请求,则会将剩余的层级请求全部丢入mCacheQueue交由CacheDispatcher进行处理。
标签:
原文地址:http://blog.csdn.net/u010878994/article/details/51855188