Volley框架剖析( 二)从开始到结束

来源:互联网 发布:网络在线咨询图片 编辑:程序博客网 时间:2024/05/22 14:32

上一篇中,我们分析了Volley的一个总体组成。今天我们继续分析Volley的一个数据流走向,即从初始化到发起请求,再到请求结束的一个流程。

先看初始化。
Volley的初始化,实际上就是返回一个RequestQueue的队列。在Volley中调用。一个最简单的创建方式即有一个Context即可。

/**     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.     *     * @param context A {@link Context} to use for creating the cache dir.     * @return A started {@link RequestQueue} instance.     */    public static RequestQueue newRequestQueue(Context context) {        return newRequestQueue(context, null);    }

我们的分析当然不是点到即止,所以,我们重点关注参数最全的构造函数。

    /**     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.     * You may set a maximum size of the disk cache in bytes.     *     * @param context A {@link Context} to use for creating the cache dir.     * @param stack An {@link HttpStack} to use for the network, or null for default.     * @param maxDiskCacheBytes the maximum size of the disk cache, in bytes. Use -1 for default size.     * @return A started {@link RequestQueue} instance.     */    public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {   File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);        String userAgent = "volley/0";        try {            String packageName = context.getPackageName();            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);            userAgent = packageName + "/" + info.versionCode;        } catch (NameNotFoundException e) {        }        if (stack == null) {            if (Build.VERSION.SDK_INT >= 9) {                stack = new HurlStack();            } else {                // Prior to Gingerbread, HttpUrlConnection was unreliable.                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));            }        }        Network network = new BasicNetwork(stack);        RequestQueue queue;        if (maxDiskCacheBytes <= -1)        {            // No maximum size specified            queue = new RequestQueue(new DiskBasedCache(cacheDir), network);        }        else        {            // Disk cache size specified            queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);        }        queue.start();        return queue;    }

这个方法有三个参数Context,HttpStack和maxDiskCacheBytes.
前面说了HttpStack是一个接口,用于Http请求的具体实现,maxDiskCacheBytes用于创建磁盘缓存时来设置最大的缓存容量。

初始化中有意思的是自定义了一个USERAGENT,这个USERAGENT用在HTTP头部中,表示当前的请求是由当前软件发起,方便日后查找问题和统计。这里也建议自己在创建网络请求的时候,用自己的UA代替。按照经验,UA通常由软件(框架)名称+版本+其他附加信息构成。

创建HTTP请求这里也区别开了SDK VERSION 在9以上和9以下。
对于9以上的版本,直接使用了HttpURLConnection,对于9以下的版本使用了AndroidHttpClient,什么原因呢,可以参考:http://android-developers.blogspot.com/2011/09/androids-http-clients.html
由于这文章需要翻墙,我这里把要点罗列如下:

1.HttpURLConnection是一个轻量级的Http连接方案,适用于大多数应用,它支持流压缩与网络缓存,能够减少网络流量与节省电池,
因此推荐Gingerbread 及以上的使用这个API。
2. 在Gingerbread以下,HttpURLConnection当在一个还没有读完的流上调用close方法时,这个流上没有读完的部分会缓存下来,然后会加到下一个请求的数据区,导致下一个请求的数据错误。
3. AndroidHttpClient复杂扩展性好且稳定,但AndroidTeam很难在保证兼容性的同时改进一些特性,因此,通常不推荐使用。

当然如果你是自己扩展之后,传的一个实现了HttpStack的对象,就不会走到上面的逻辑中。

在根据版本获取了HttpStack对象后,将其作为参数传给Network的默认实现BasicNetwork对象。即BasicNetwork中实际上是对HttpStack的一个包装(当然,如果自己实现了其他的Network,就不一定需要这个HttpStack)。

之后,根据maxDiskCacheBytes创建了一个磁盘缓存。

最后,启动这个RequestQueue。
启动时做了两件事,创建CacheDispatcher和NetworkDispatcher。这两个都是线程类的子类,CacheDispatcher 的线程主要在作用是本地的IO操作,加载缓存用。NetworkDispatcher的线程主要执行网络操作,默认创建了1个缓存线程和4个网络线程。

在初始化完成之后,就可以使用RequestQueue来发起网络请求。

发起请求从RequestQueue的add方法开始。

这里写图片描述
http://img.blog.csdn.net/20150425163836789

流程如上图,是比如简单的。这里说几个有趣细节。
在add的第一行代码,我们看到 request把RequestQueue传到了对象中,并作为一个成员变量保存。那为什么?因为request的生命周期是由request的一些方法来维护,此后就与这个queue无关了,但是在一个request又需要在结束时,把自己从queue中移除掉,它需要 这个引用来做移除操作。可以参见request.finish的代码。

 if (mRequestQueue != null) {            mRequestQueue.finish(this); }

每个请求,有一个唯一的序列号,这个是用AtomicInteger来实现的。

private AtomicInteger mSequenceGenerator = new AtomicInteger();  /**     * Gets a sequence number.     */ public int getSequenceNumber() {        return mSequenceGenerator.incrementAndGet(); }

在没有主动设置优先级的情况,这个是做为request的优先级来处理的。即数字越小,优先级越高。

还有个有意思的点是request的日志系统,所有的状态改变,都会有事件记录,以便跟踪问题。
这里写图片描述

从上图可以看出,比如缓存命中情况,网络请求情况,重试情况等,都有一一记录。

volley避免重复请求节约网络资源的逻辑写得很有技巧。这里巧妙的利用了一个空的value来处理,可以少生成一个链表对象。

  // Insert request into stage if there's already a request with the same cache key in flight.        synchronized (mWaitingRequests) {            String cacheKey = request.getCacheKey();            if (mWaitingRequests.containsKey(cacheKey)) {                // There is already a request in flight. Queue up.                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);                if (stagedRequests == null) {                    stagedRequests = new LinkedList<Request<?>>();                }                stagedRequests.add(request);                mWaitingRequests.put(cacheKey, stagedRequests);                if (VolleyLog.DEBUG) {                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);                }            } else {                // Insert 'null' queue for this cacheKey, indicating there is now a request in                // flight.                mWaitingRequests.put(cacheKey, null);                mCacheQueue.add(request);            }

如果是第一次请求,直接放入一个空value,第二次如果是相同的请求,才会增加一个链表来缓存相同的request。当第一个请求网络完成后,它才会把这个链表中的请求通通加到缓存队列中。这样,即可以保证即时很短时间内的并发相同请求,实际只有一个才会使用网络资源,又能保证相同的请求不会被丢弃。

volley的很多细节还是很值得我们学习。下一次我们对一些重要的类进行分析。

0 0
原创粉丝点击