当前位置: 代码迷 >> Android >> [Android] Volley源码分析(1)体系结构
  详细解决方案

[Android] Volley源码分析(1)体系结构

热度:389   发布时间:2016-04-28 04:57:12.0
[Android] Volley源码分析(一)体系结构

       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设计。






  相关解决方案