上一篇我们说到了项目里面关于volley框架实战的一些应用。知其然,知其所以然。我们一定要明白volley框架比我们平时自己去写的网络操作好在哪里。
本篇主要是我在参看了volley框架源码的之后,对volley框架的认识。如果你希望得到详细的代码理解。请参照这个链接,http://www.cnblogs.com/spec-dog/p/3821417.html。和这个链接http://blog.csdn.net/yuanzeyao/article/details/25837897 都讲的很好。
我主要是说下volley框架的几个我觉得比我们平时自己写网络请求很好的地方。
1.优良的架构设计:
volley框架采用的传统的观察者模式(生产者消费者模式)来实现网络的请求的。下面是整个volley框架的架构解析:
volley框架的分成了四个阶层:请求,队列和调度,实现网络通信,返回请求结果。我们分别说这四个阶层都干了什么?
请求:采用传统的继承体系,把http请求抽象成一个抽象类,把http的报文结构都抽象成为方法,比如getHead(),getURL(),getBody(),getParams(),当然,这里还含有第四个阶段的回调监听的方法deliverReponse(T reponse),采用设置外部实现返回监听接口的方式。
队列和调度:解决了采用什么样的数据结构每个请求都是一个都会放入一个请求队列中(blockQueue),然后采用开启子线程的方式(android也不运行网络请求在主线程中)来从每个请求队列中获取请求对象。实现请求。这里还做了网络请求的缓存。(这是volley框架的核心)
网络通信的实现:这一块可能和我们平时自己开发的框架基本一直,就是在分发调度的线程中实现了网络请求。网络请求采用HttpURLconnetion和HttpClient两种请求。(根据android的版本号不同来采用对应的请求方式,2.3之前用的是httpcllient,之后用HttpURLconnetion)。
返回请求结果:同请求类中的deliverReponse方法对应,实质也就是交给在activity(或者是主线程中)提前设置好了监听器,然后让在外部设计好的监听器接口,来实现处理返回结果的功能。这一步也是在分发的先
以上就是整个Volley框架的架构:其实这个架构简单又容易理解。其实采用的最大亮点就是把http请求的每一部都细化成了一个个模块,比如把请求演化成了继承体系,把请求形成数据结构(队列)并且采用调度的方式,采取实现监听的模式,来实现对返回结果的处理。
2.良好的设计模式
当然,设计模式同架构是息息相关的。大致了看了下volley框架所用的设计模式:其实感觉最多的就是观察者模式,请求队列是一个主题,当每次来了请求之后,请求队列都会通知调度线程,分发请求给线程,来实现操作响应。还有就是针对接口编程,不是针对实现编程。比如在request的的继承体系中,框架没有针对实现编程,而是把每个HTTP步骤都细化成了一个个抽象方法,通过stirngRquest,jsonRequest等子类去实现,也可以让我们自己去实现,体现了框架的灵活性。这真的是开闭原则的一种很好的体现。
3.稳定的网络请求操作
我们平时其实实现网络请求也是用HttpURLconnection和HttpClient的两个类来实现的,但是为什么我们没有写到那么好呢?其实原因在于我们没有考虑的那么全面。其实volley框架的中关于网络的部分,和我们平时的调用是一样的。volley框架稳定的原因在于做了很多的异常处理。我们查看下框架网络请求部分的源代码:
public NetworkResponse performRequest(Request> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map responseHeaders = new HashMap();
try {
// Gather headers.
Map headers = new HashMap();
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);
}
}
}
}
这个类是封装请求类的类,其实,只有上面那段标红加粗了的代码是我们的网络请求的部分的。其他的部分呢?都是再做异常的判定,现在明白了我们平时写的网络请求为什么那么不稳定了吧,基本很少考虑这些异常,即使是捕捉了,我们也没有怎么做异常处理。
查看下源代码吧,我们可以看到,这里不仅仅有对URL错误,三次握手超时,返回体超时,网络I/O错误等异常,也有关于http返回状态码的异常:比如404,503等错误状态码等异常处理,甚至包括了网路中反生错误之后返回体为空等情况,避免了android中常见的空引用异常。
我们可以看到,volley框架稳定的原因的之一就是这些异常处理处理做得很好。导致了网络请求十分稳定,调用者使用的时候不会发生什么其他的异常。这是volley框架很好的一个地方。
4.基于handler机制来把监听器中的方法回调给主线程操作
我们平时写的网络操作都是把handler机制明显化的,比如我们喜欢把handler传入线程中,然后在activity中写内部类handler。这样也可以。但是后期维护起来很麻烦。volley是怎么做的呢?这里其实很简单: 在Volley初始化一个RequestQueue的时候,会调用RequestQueue的如下构造函数,它构建了一个ExecutorDelivery对象,并把一个与主线程的Looper关联的一个Handler。源代码如下
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
2 this(cache, network, threadPoolSize,
3 new ExecutorDelivery(new Handler(Looper.getMainLooper())));
4 }
然后再看下ExecutorDelivery的构造方法, 通过handler的post方法,把ResponseDeliveryRunnable 这个runnable加到了主线程的消息队列中,所以它的run()方法是在主线程中执行的。
public ExecutorDelivery(final Handler handler) {
2 // Make an Executor that just wraps the handler.
3 mResponsePoster = new Executor() {
4 @Override
5 public void execute(Runnable command) {
6 handler.post(command);
7 }
8 };
9 }
这就是volley框架中回调监听接口的方法。