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


thx

非子墨

QQ:1025250620





[Android] Volley源码分析(一)体系结构,布布扣,bubuko.com

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

上一篇:Android开发之UI更新交互机制与实例解析


下一篇:iOS应用的真机调试