Volley:google出的一个用于异步处理的框架。由于本身的易用性和良好的api,使得它能得以广泛的应用。我还是一如既往从源码的方向上来把控它。我们先通过一段简单的代码来了解Volley
[java] view plaincopy
RequestQueue queue = Volley.newRequestQueue(this);
ImageRequest imagerequest = new ImageRequest(url, new Response.Listener<Bitmap>(){
@Override
public void onResponse(Bitmap response) {
// TODO Auto-generated method stub
System.out.println(">>>bitmap = "+response);
}}, 300,
200,
Config.ARGB_8888,
new ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub
System.out.println("error "+error);
}
});
queue.add(imagerequest);
俗话说,“对比方显美”,代码框架也如此。我们先回想一下我们过去创建bitmapcache的惨痛经历吧。首先我们使用的cache功能比较单一,往往只是支持内存cache,淘汰策略一般是通过数量或者容量限制。每写一个app都自成一套。此外,一旦我们脱离了程序,我们将不再获得我们Bitmap的元数据,比如请求网络链接,资源描述符等等,而且对于同一个网络请求我们要用单独的装饰器来拦截。
当然,我之所以列举这些出来,是因为在Volley里面已经很好的解决了这些问题,当你下载了Volley的源码编译以后,你会发现,Volley所涵盖的功能远比你考虑的要多。而且这些东西,已经被很好的封装起来。而且Volley的代码读起来也非常的顺口,并不像Android原生的一些代码一样又臭又长。如果说Volley是一种好的开源框架,不如说Volley是一套现在看起来还不错的设计模式。而且从Volley所提供的有些接口来说,Volley已经将很大部分封装在框架内部,对于api调用者来说,无疑是个福音。
Volley里面普遍采用了生产消费者模式,当然你说它是观察者其实也无可厚非。生产者通过生产产品来通知消费者消费,这种简单的模型在多种框架中使用到。这里的消费者是叫Request的类。这个类将分发给CacheDispatcher和NetworkDispatcher来进行消费。从我们写Cache的经验来说我们对Cache的处理模式普遍一致,大都是看Cache中是否存在,如果存在,则load from disk.否则请求网络。其实Volley的处理大同小异。对于允许Cache的请求。Request将被RequestQueue分发到CacheDispatcher消费。如果CacheDispatcher消费不了,那么就将分发给NetworkDispatcher。这种模式非常像职责链。其实在这种分发下,存在一个Volley的亮点之一,就是url拦截。RequestQueue本身维护一个暂存列表,而这种暂存列表能很好的拦截重复的URL请求。由CacheDispatcher处理的请求,如果在disk cache中存在,那么将通过Request的parseNetworkResponse 接口进行解析,我们看一下对于一个图片请求他的parse流程:
[java] view plaincopy
private Response<Bitmap> doParse(NetworkResponse response) {
byte[] data = response.data;
BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
Bitmap bitmap = null;
if (mMaxWidth == 0 && mMaxHeight == 0) {
decodeOptions.inPreferredConfig = mDecodeConfig;
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
} else {
// If we have to resize this image, first get the natural bounds.
decodeOptions.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
int actualWidth = decodeOptions.outWidth;
int actualHeight = decodeOptions.outHeight;
// Then compute the dimensions we would ideally like to decode to.
int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
actualWidth, actualHeight);
int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
actualHeight, actualWidth);
// Decode to the nearest power of two scaling factor.
decodeOptions.inJustDecodeBounds = false;
// TODO(ficus): Do we need this or is it okay since API 8 doesn't support it?
// decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED;
decodeOptions.inSampleSize =
findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);
Bitmap tempBitmap =
BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
// If necessary, scale down to the maximal acceptable size.
if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth ||
tempBitmap.getHeight() > desiredHeight)) {
bitmap = Bitmap.createScaledBitmap(tempBitmap,
desiredWidth, desiredHeight, true);
tempBitmap.recycle();
} else {
bitmap = tempBitmap;
}
}
if (bitmap == null) {
return Response.error(new ParseError(response));
} else {
return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response));
}
}
我们看到,它产生了一个response对象返回给了上层,最后再由Delivery来分发Reponse.其实我们可以看出,整个Volley的Cache设计相对简单,而且还有很大的改造空间。比如,对于Delivery的分发机制,完全可以用EventBus这类的事件驱动的框架来完成。还有Bitmap的生成,可以采用内存映射文件的方式来减少内存开销。当然,这些小甜点并不影响Volley做为一个非常优秀的代码而存在。在Volley里面Delivery的本质是一个线程池,采用线程池post的方式可以有效的避免Volley的CacheDispatcher和NetWorkDispatcher因为处理reponse而造成的线程阻塞。
我们再回头看看如果我们在Cache中不存在,我们请求网络的情况。Volley对平台的请求接口进行了封装,你可能采用的是HttpClient,当然也有可能是直接使用HttpUrlConnection。对于上层来说,为了屏蔽掉这种平台的差异性,抽象出一个叫做Network的网络接口,这是一种桥接的模式。而当你使用某种方式网络获得数据以后,NetworkDispatcher将数据put到Cache中.通过Delivery分发Request回调以后,调用Request的finish方法将自己从RequestQueue的暂存表中删除。
好的,是不是觉得Volley代码设计的是如此的思路清晰,本章我们先介绍Volley的主体结构,下面会通过两章节来描述Volley的两大模块:Cache和Network设计。