一些关于volley分享 扩展分享
关于volley基本用法、具体如何请求数据本文不做解释,本次目的是一起共同学习volley为我们做的一些优化。
- 源码简单分析
- 实用扩展
一、源码简单分析
- 首先看看volley的入口也就是Volley的newRequestQueue方法。
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) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
}
2.上面重载两个newRequestQueue()方法。
DiskBasedCache配置请求结果的缓存目录等。
指定请求方式network,一般情况下:采取系统版本来默认请求方式,基于Android2.3及以上使用HttpUrlConnection,以下则采用httpClient,httpClient在Android6.0中已经被移除,需要另外引入httpClient的jar包。
3.通过Volley的newRequestQueue方法返回一个RequestQueue对象,并调用一个start()方法
那我们看下RequestQueue类。先来了解一下下面这两个比较重要的属性:
private final PriorityBlockingQueue> mCacheQueue =
new PriorityBlockingQueue>();
存放需要从缓存获取结果的Requse队列
private final PriorityBlockingQueue> mNetworkQueue =
new PriorityBlockingQueue>();
存放需要从网络获取结果的Rquest队列
PriorityBlockingQueue: 当没有元素退栈的时候会阻塞线程,即执行take()发现没有Request了则阻塞线程
4.我们再来看看RequestQueue的构造方法,有好几个,我们看最终调用的,框架默认配置了哪些。
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
默认情况下:从RequestQueue构造方法这里看,Volley之所以可以在请求结果回调监听里面更新UI是因为将结果交给主线程的handler处理,稍候再看ExecutorDelivery。
5.再来看看RequestQueue的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();
}
}
从上一段代码来看 开启一个从缓存读取结果的线程mCacheDispatcher以及开启四个从网络读取的线程mNetworkDispatcher。 当然也可以通过构造函数指定要开启的个数,建议根据设备配置设定开启个数。
6.既然框架给我们默认启动两种线程。那我们来看看线程的run()方法做了写什么,先从mCacheDispatcher的run()方法开始。
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
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;
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
}
}
});
}
} catch (InterruptedException e) {
if (mQuit) {
return;
}
continue;
}
}
}
上面一段代码大概意思就是开个死循环不停的从缓存对列里面抽取Request执行 当没有Request可以执行的时候则当前线程阻塞,即mCacheQueue.take()方法会阻塞,如果当前request没有取消,继续判断本地有缓存且没有过期,直接从缓存文件中获取结果mDelivery.postResponse()到主线程,否者添加到网络请求队列中执行网络请求。
我们再来看看网络请求线程NetworkDispatcher的run()方法做了写什么
@Override
public void run() {
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");
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
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");
}
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);
}
}
}
上面一段代码大概意思也是开个死循环不停的从网络请求队列里面抽取Request,执行 当没有Request可以抽取的时候则当前线程阻塞, 如果当前request没有被取消。继续执行mNetwork.performRequest方法从网络获取结果,从网络上获取结果后,根据配置的Request是否需要缓存,再mDelivery.postResponse()到主线程。
7.接下来我们看看是如何将返回结果返回到主线程的 上面也讲到构造RequestQueue时候同时创建了一个ExecutorDelivery 构造参数是主线程的handler,那我们具体看下ExecutorDelivery类
public class ExecutorDelivery implements ResponseDelivery {
private final Executor mResponsePoster;
public ExecutorDelivery(final Handler handler) {
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
public ExecutorDelivery(Executor executor) {
mResponsePoster = executor;
}
@Override
public void postResponse(Request> request, Response> response) {
postResponse(request, response, null);
}
@Override
public void postResponse(Request> request, Response> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
@Override
public void postError(Request> request, VolleyError error) {
request.addMarker("post-error");
Response> response = Response.error(error);
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
}
mResponsePoster实现了Executor接口并覆盖了execute方法。execute()方法中调用handler.post(command),把任务post到主线程执行。
postResponse中实现ResponseDeliveryRunnable任务
从ResponseDeliveryRunnable的run方法看
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
将处理结果交给Request处理,正确响应调用deliverResponse()和错误响应调用deliverError()。
8.再来说一下Request类,它是一个抽象类再看看2个抽象方法parseNetworkResponse和deliverResponse方法
parseNetworkResponse解析回来的结果,deliverResponse处理返回的结果,像子类jsonRequst和StringRequest都有不同的实现,一般情况下我们也就继承Requst来解析和处理成我们想要的结果。就官方给我们一个实现类来看
@Override
protected Response parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
把返回的字节response.data返回然后转换成字符串在以JSONObject的形式返回。
接着就可以然后在里面通过回调监听返回数据结果,类似于
public void deliverError(VolleyError error) {
if (mErrorListener != null) {
mErrorListener.onErrorResponse(error);
}
}
9.接下来我们看看Request怎么添加到队列里面的,看下面一段代码
public Request add(Request request) {
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
Queue> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else { mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
大概意思先将Request放到一个set集合中,方便取消等操作,再判断这个request有没有标记需要缓存,如果不需要缓存则添加到networkQueue中并跳出方法,否者放到cacheQueue中,还有一段添加到cacheQueue中比较难理解的代码,当执行获取缓存的时候又有相同URL的Request来请求缓存,则会放在等待队列WaitingRequests中,等上一个相同URL的request请求执行完成后才会一次性全部添加到cacheQueue中。
总结一下:从源码中看,volley很容易扩展,面向接口编程,解耦。也帮助我们维护请求队列以及排队机制,省去很多麻烦等等。
缺点就是不能够很好的下载和上传,原因可能是要把数据源全部转换成byte数组,大文件读进内存花销太大。当然我们也可以根据需要进行扩展。