Volley:google出的一个用于异步处理的框架。由于本身的易用性和良好的api,使得它能得以广泛的应用。我还是一如既往从源码的方向上来把控它。我们先通过一段简单的代码来了解Volley
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流程:
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设计。