本文是对Volley思路的整体整理,并不是Volley教程,建议有Volley使用经验,但是对Volley整体不是很清楚的同学阅读。
我认为,弄清整体的流程很重要,以避免一叶障目不见泰山的囧境,而对于面向对象编程,弄清每个类是干什么的,类与类之间的关系后,就不难搞懂整个流程了。
所以本文不会深入源码细节,从每个类的构造参数最全的构造函数入手,讲解这个类是干什么的,由什么构成,每个元素的作用是什么。
因为不会深入细节,所以建议最好了解Volley的基本使用方法。
1.Volley类初始化RequestQueue
在整个Volley流程里,原始类是Volley,这个类中有一个静态方法会new一个RequestQueue,而一切都从这里开始
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) {
}
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;
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;
}
RequestQueue
可以看到我们首先根据SDK版本来选择合适的HTTP请求方案,版本大于等于9的时候使用HTTPUrlConnection进行请求。
根据你请求方案来初始化我们的RequestQueue,看一下RequestQueue的构造:
public RequestQueue(Cache cache,
Network network,
int threadPoolSize,
ResponseDelivery delivery) {/*...*/}
四个参数:缓存,请求方案,网络连接池大小,请求结果分发器
缓存:Volley默认使用DiskBasedCache
请求方案:HttpClient和HttpUrlConnection两种
网络连接池:即建立几个网络请求线程NetWorkDispatchThread,默认的大小是4
请求结果分发器:当我们获得请求结果后,有它分发出去,通常回调我们之前定义的处理方法。
剩下的细节暂且不表,建立了RequestQueue后,我们会获得两个队列,缓存请求队列和网络请求队列,那由谁来到队列里取出请求呢?
2.建立线程处理请求队列,两种请求对应两种线程
public CacheDispatcher(BlockingQueue<Request<?>> cacheQueue,
BlockingQueue<Request<?>> networkQueue,
Cache cache, ResponseDelivery delivery) {/*...*/}
public NetworkDispatcher(BlockingQueue<Request<?>> queue,
Network network,
Cache cache,
ResponseDelivery delivery){/*...*/}
CahceDispathcher,即缓存请求处理线程,四个初始化参数,这里看到第二个参数是:networkQueue。咦?既然是处理缓存请求的线程,为什么要在这里传入网络请求队列呢?
从cachequeue中拿到请求,根据请求信息去chche中获取,如果缓存没问题,则通过delivery分发,如果我们会遇到缓存过期,或者缓存没过期但是需要更新的情况,这个时候就需要把请求放入networkqueue中,以供下一步请求。
NetworkDispatcher,即网络请求处理线程,也是四个初始化参数:从queue中获得请求,有network方案执行请求,返回结果后如果可以缓存则放入cache,最后将返回的结果通过delivery送到处理的地方。
3.由HttpStack进行网络工作
cache部分就像上面说的那样,最后回到网络部分,这部分请求工作就需要单独说一说了。
public interface HttpStack {
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
}
public HttpClientStack(HttpClient client) {/*...*/}
public HurlStack(UrlRewriter urlRewriter,
SSLSocketFactory sslSocketFactory){/*...*/}
在HttpStack接口中,只有一个要实现的方法:performRequest。顾名思义,就是执行请求的意思,我们所有的Request都要实现这个方法,根据请求的不同网络请求执行过程有很大不同。
有两个参数,一个是请求本身,还有一个是附加的头信息,在Volley中,根据不同的请求阶段和请求状态,我们会定义不同的附加说明信息,这些信息会放在header里。
如上文所说,根据SDK版本不用,我们使用不同的请求方案,在这里对应的就是HttpClentStack和HurlStack了。
剩下的就是android的网络请求知识,在本篇文章中不是重点。
总之最后我们会拿到请求的回应response,而在不同的请求中,由于数据格式不同,我们需要将response返回的二进制字节流转化成需要的格式,
这时候就要调用resquest的 request.parseNetworkResponse(networkResponse)方法,解析response,按照要求解析之后,我们拿到的才是自习想用的数据,如:BitMap、Json等等,这才是我们真正需要要的Response,然后呢?
4.Delivery将Response运走
public interface ResponseDelivery {
public void postResponse(Request<?> request, Response<?> response);
/**
* 解析从网络或者缓存中获取的响应并分发出去。
* 所定义的的线程将在分发后执行
*/
public void postResponse(Request<?> request, Response<?> response, Runnable runnable);
/**
* Posts an error for the given request.
*/
public void postError(Request<?> request, VolleyError error);
}
在其具体实现类ExecutorDelivery中,无论如何都会执行runnable,即使初始化为null。因为我们将最后的结果处理统一放在了那个线程里,简化版源码如下所示
public class ExecutorDelivery implements ResponseDelivery {
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
private class ResponseDeliveryRunnable implements Runnable {
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
//...
}
public void run() {
//...
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
//...
// If we have been provided a post-delivery runnable, run it.
if (mRunnable != null) {
mRunnable.run();
}
}
}
}
可以看到,最后调用的是mRequest.deliverResponse,这个才是处理了最后求的方法,所以,我们需要关注一下这些请求本身了。
5.各种请求
上面就是大概的请求分发过程,至于具体的处理请求方法就涉及到HTTP的知识了,此处都不细讲,过两天会专门结合HTPP语义和Volley来写一篇文章。
Volley有一大特性就是灵活,因为Volley设计中有一个很重要的思想是“面向接口编程”,我们可以定制符合自己要求的Request,Request接口中这样初始化一个Request:
public Request(int method,
String url,
Response.ErrorListener listener){/*...*/}
三个参数:请求方法(GET、PUT、POST…),请求的URL,请求错误的监听器。
也就是说,我们至少要让自己的请求具备这个三个参数,剩下的就随意我们扩展了,而Volley也为大家提供了几种定制好的请求,下面就来看一看吧。
public ImageRequest(String url,
Response.Listener<Bitmap> listener,
int maxWidth,
int maxHeight,
ScaleType scaleType,
Config decodeConfig,
Response.ErrorListener errorListener) {/*...*/}
public JsonArrayRequest(String url,
Listener<JSONArray> listener,
ErrorListener errorListener) {/*...*/}
public JsonObjectRequest(int method,
String url,
JSONObject jsonRequest,
Listener<JSONObject> listener,
ErrorListener errorListener) {/***/}
public StringRequest(int method,
String url,
Listener<String> listener,
ErrorListener errorListener) {/*...*/}
public JsonRequest(int method,
String url,
String requestBody,
Listener<T> listener,
ErrorListener errorListener){/*...*/}
最复杂的就是ImageRequest,这里初始化没有method,因为默认是使用GET方法的,随后定义了结果监听器,最大宽度,最大高度,缩放方式,解码配置,错误监听,这个就涉及到在Android中加载图片需要注意的事项了,也会写一篇单独的文章讲一下。
后面的几种,他们的参数顾名思义就可以了,哈哈,我就偷个懒,不讲了。
为什么要单独将这些请求列出来呢?
因为有一个很重要的参数,这个参数的存在,将Volley整个流程形成的闭环,那就是,上述所有构造器中都有的一个参数: Lister<T> listener
(T表示请求数据的类型)
这个Listener就用来接收delievery分发回来的结果数据,对数据进行处理,这个listener需要由我们自己重写,按照想要的方式展示结果数据。
在第4小节中提到了mRequest.deliverResponse的方法,此时可以看看它的真面目了,此处以ImageQequest为例:
@Override
protected void deliverResponse(Bitmap response) {
mListener.onResponse(response);
}
这回大家都明白了吧。
一切都自Request始,到这里又有request终。
回过头来一看,是这样的一个过程:
START
1.定义你的Request,并定义好相应请求结果的回调Listener
2.建立RequestQueue
3.将你的Request放入RequestQueue中(RequestQueue.add())
4.由cacheDispatcher和networkDispatcher线程进行请求的分发
5.按照请求的性质,有cache或者HttpStack进行处理请求,获取返回的数据
6.处理返回数据,该放入缓存的放入缓存,然后使用ExecutorDelivery将结果分发出去。
7.调用Request的Listener,接受并处理Delivery给我们分发回来的结果。
END
本文尽量从一个清晰的思路讲解Volley的流程,从中体会Volley的设计思想。
为了清晰简洁,除了ExecutorDelivery以为,并没有深究细节,但是希望看完后可以去仔细研究Volley的细节,因为不研究细节是无法总结出来整体流程的。我也是翻来覆去看了几遍源码,才总结出来的这篇文章。
有浅入深,而后深入浅出。
这是学习Volley的过程中最大的心得,Volley并不难懂,难的是将从Volley学到思想用在自己的程序中,这就需要多多练习了。
如果有错误的地方,麻烦大家指正。
如需转载,请注明原作:
?