码迷,mamicode.com
首页 > 其他好文 > 详细

Volley源码解析

时间:2016-07-13 16:13:20      阅读:124      评论:0      收藏:0      [点我收藏+]

标签:

概念:
Google在I/O 2013大会上发布了Volley。它是Android平台上的网络通信库,能使网络通信更快,更简单,更健壮。
1、优点就是特别适合数据量不大但是通信频繁的场景
2、缺点就是大数据传输表现的很糟糕。
官方开源地址:
Volley Android Developer文档:http://developer.android.com/training/volley/index.html

Volley主页:https://android.googlesource.com/platform/frameworks/volley

Volley仓库:Git clone https://android.googlesource.com/platform/frameworks/volley

Volley GitHub Demo:在GitHub主页搜索Volley会有很多.

一图胜千言(来源于Volley Android Developer文档):

技术分享

下面 let me 娓娓道来:

1、首先我们来看下Volley.java类:

public class Volley {
    /** Default on-disk cache directory. */
    private static final String DEFAULT_CACHE_DIR = "volley";
    /**
     * 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.
     * @param stack An {@link HttpStack} to use for the network, or null for default.
     * @return A started {@link RequestQueue} instance.
     */
    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        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 = new RequestQueue(new DiskBasedCache(cacheDir), network);
        queue.start();
        return queue;
    }
    /**
     * 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);
    }
}

使用Volley的第一步首先是通过Volley.newRequestQueue(context)得到RequestQueue队列,
这个方法默认调用了第二个重载它的方法;并且HttpStack 参数默认传null,也就是说if (stack == null) 默认会执行,
然后就判断android的版本号:
api>=9就使用HurlStack(也就是HttpURLConnection);
api<9就使用HurlStack(也就是HttpClient );
也就验证了下面这句话:
默认 Android2.3 及以上基于 HttpURLConnection,2.3 以下基于 HttpClient 实现!
究其原因,是在 Froyo(2.2) 之前,HttpURLConnection 有个重大 Bug,调用 close() 函数会影响连接池,导致连接复用失效。
接着,我们看第29行,HttpStack创建完成之后创建了Network实例。BasicNetwork是Network接口的实现,他们都在toolbox中,其作用是根据传入的HttpStack对象来处理网络请求。

2、我们先看下NetWork接口的代码:

/**
 * 用于执行请求的接口。
 */
public interface Network {
    /**
     * Performs the specified request.
     * @param request Request to process
     * @return A {@link NetworkResponse} with data and caching metadata; will never be null
     * @throws VolleyError on errors
     */
    public NetworkResponse performRequest(Request<?> request) throws VolleyError;
}

它的BasicNetwork实现子类如下:

 @Override
    public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();//用于标记响应速度的时间。
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map<String, String> responseHeaders = Collections.emptyMap();
            try {
                // 添加请求的消息头。
                Map<String, String> headers = new HashMap<String, String>();
                addCacheHeaders(headers, request.getCacheEntry());
                httpResponse = mHttpStack.performRequest(request, headers);
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();

                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                // 处理缓存验证。
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {

                    Entry entry = request.getCacheEntry();
                    if (entry == null) {
                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
                                responseHeaders, true,
                                SystemClock.elapsedRealtime() - requestStart);
                    }
                    //一个HTTP304响应不具有所有消息头,我们必须从新的响应中使用高速缓存条目使用的消息头。
                    entry.responseHeaders.putAll(responseHeaders);
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                            entry.responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart);
                }

                // 有一些响应如 204S 没有内容,我们必须检查。
                if (httpResponse.getEntity() != null) {
                  responseContents = entityToBytes(httpResponse.getEntity());
                } else {
                  // 如果没有内容,我们就添加0字节来替换null.
                  responseContents = new byte[0];
                }

                // 如果响应特别慢,我们会LOG出来。
                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                logSlowRequests(requestLifetime, request, responseContents, statusLine);

                if (statusCode < 200 || statusCode > 299) {
                    throw new IOException();
                }
                return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
                        SystemClock.elapsedRealtime() - requestStart);
            } catch (SocketTimeoutException e) {
                attemptRetryOnException("socket", request, new TimeoutError());
            } catch (ConnectTimeoutException e) {
                attemptRetryOnException("connection", request, new TimeoutError());
            } catch (MalformedURLException e) {
                throw new RuntimeException("Bad URL " + request.getUrl(), e);
            } catch (IOException e) {
                int statusCode;
                if (httpResponse != null) {
                    statusCode = httpResponse.getStatusLine().getStatusCode();
                } else {
                    throw new NoConnectionError(e);
                }
                VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
                NetworkResponse networkResponse;
                if (responseContents != null) {
                    networkResponse = new NetworkResponse(statusCode, responseContents,
                            responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
                    if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                            statusCode == HttpStatus.SC_FORBIDDEN) {
                        attemptRetryOnException("auth",
                                request, new AuthFailureError(networkResponse));
                    } else if (statusCode >= 400 && statusCode <= 499) {
                        // Don‘t retry other client errors.
                        throw new ClientError(networkResponse);
                    } else if (statusCode >= 500 && statusCode <= 599) {
                        if (request.shouldRetryServerErrors()) {
                            attemptRetryOnException("server",
                                    request, new ServerError(networkResponse));
                        } else {
                            throw new ServerError(networkResponse);
                        }
                    } else {
                        // 3xx? No reason to retry.
                        throw new ServerError(networkResponse);
                    }
                } else {
                    attemptRetryOnException("network", request, new NetworkError());
                }
            }
        }
    }

这个方法是网络请求的具体实现,其中**第12行**mHttpStack.performRequest(request, headers);代码中的mHttpStack是Volley的newRequestQueue()方法中创建的实例。创建完实例后,紧接着new出一个RequestQueue对象,并调用它的start()方法进行启动。
3、下面我们看下它的start方法:

//在启动此队列调度。
public void start() {
        stop();  // 确保任何正在运行的调度程序停止。
        // 创建缓存调度并启动它。
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();

        // 循环创建网络调度(以及相应的线程)。
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

这里的CacheDispatcher和NetworkDispatcher都是继承自Thread的,而默认情况下for循环会执行(DEFAULT_NETWORK_THREAD_POOL_SIZE)四次,也就是说当调用了Volley.newRequestQueue(context)之后,就会有五个线程一直在后台运行,不断等待网络请求的到来,其中一个CacheDispatcher是缓存线程,四个NetworkDispatcher是网络请求线程。
4、所以先看下CacheDispatcher的run方法:

@Override
    public void run() {
        if (DEBUG) VolleyLog.v("start new dispatcher");
        //设置线程的优先级,默认为10.
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        // 使阻塞调用初始化缓存。
        mCache.initialize();

        while (true) {
            try {
                //获得从缓存队列中分流的请求,直到阻塞
                //至少有一个是可用的。
                final Request<?> request = mCacheQueue.take();
                request.addMarker("cache-queue-take");

                // 如果这个请求已经被取消了,不打扰它调度。
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }

                // 尝试从缓存中获取此条目。
                Cache.Entry entry = mCache.get(request.getCacheKey());
                if (entry == null) {
                    request.addMarker("cache-miss");
                    // 缓存条目为空;派发到网络调度。
                    mNetworkQueue.put(request);
                    continue;
                }

                // 如果缓存条目过期了,将其发送到网络调度。
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                }

                // 有缓存条目,则解析其数据递送回请求。
                request.addMarker("cache-hit");
                Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                request.addMarker("cache-hit-parsed");

                if (!entry.refreshNeeded()) {
                    // 完全未过期的缓存条目,传送至响应结果中。
                    mDelivery.postResponse(request, response);
                } else {
                    // 软过期的缓存条目。我们可以提供缓存的响应,
                    //但是我们还需要将请求发送到网络去更新。
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);

                    // 标记反应中间体。
                    response.intermediate = true;

                    // post在中间的响应返回给用户并
                    //递送然后沿向网络转发请求。
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {

                            }
                        }
                    });
                }

            } catch (InterruptedException e) {
                // 我们可能被中断,因为是时候退出了。
                if (mQuit) {
                    return;
                }
                continue;
            }
        }
    }

这里,我们看到它首先拿到一个缓存条目,判断它是否为null和是否过期来决定是否使用缓存条目。有缓存条目,则解析其数据递送回请求;否则就发送到网络调度。最后执行了mDelivery.postResponse(request, response);
5、同理,我们来分析一下NetworkDispatcher的run方法:

 @Override
    public void run() {
        //设置线程的优先级,默认为10.
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            Request<?> request;
            try {
                // 直接拿从队列中的请求。
                request = mQueue.take();
            } catch (InterruptedException e) {
                // 我们可能被中断,因而退出了。
                if (mQuit) {
                    return;
                }
                continue;
            }

            try {
                request.addMarker("network-queue-take");

                // 如果请求已经取消,则不执行网络请求了。
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }

                addTrafficStatsTag(request);

                // 执行网络请求。
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");

                // 如果服务器返回304,而且我们已经发表了回应,
                // 我们就大功告成了(不提供第二个相同的反应)。
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

                // 这里解析工作线程的响应。
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

                // 写缓存(如果适用)。 
                // TODO:只有更新缓存元数据,而不是整个记录304S。
                if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

                // 发布响应返回。
                request.markDelivered();
                mDelivery.postResponse(request, response);
            } catch (VolleyError volleyError) {
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                parseAndDeliverNetworkError(request, volleyError);
            } catch (Exception e) {
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
                VolleyError volleyError = new VolleyError(e);
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                mDelivery.postError(request, volleyError);
            }
        }
    }

我们看到,网络调度很直接了断的执行网络请求,并处理响应; 最终也把响应的情况交给了它: mDelivery.postResponse(request, response);
6、所以接下来我们统一看一下ExecutorDelivery(ResponseDelivery接口的实现类)的postResponse():

 @Override
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
        request.markDelivered();
        request.addMarker("post-response");
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
    }

这里可以看见在mResponsePoster的execute()方法中传入了一个ResponseDeliveryRunnable对象,就可以保证该对象中的run()方法就是在主线程当中运行的了,我们看下run()方法中的代码是什么样的:

@Override
        public void run() {
            // 如果该请求被取消,完成它,但也不对外回调提供。
            if (mRequest.isCanceled()) {
                mRequest.finish("canceled-at-delivery");
                return;
            }

            // 这取决于,提供正常响应或错误。
            if (mResponse.isSuccess()) {
                mRequest.deliverResponse(mResponse.result);
            } else {
                mRequest.deliverError(mResponse.error);
            }

            //如果这是一个中间响应,添加标记,否则,我们就大功告成了,请求就可以完成。
            if (mResponse.intermediate) {
                mRequest.addMarker("intermediate-response");
            } else {
                mRequest.finish("done");
            }

            // 如果我们已经提供了一个子线程运行的,运行它。
            if (mRunnable != null) {
                mRunnable.run();
            }
       }

这段代码是最核心的,明显可以看到通过mRequest的deliverResponse或者deliverError将反馈发送到回调到UI线程。
这也是你重写实现的接口方法。
总结:
1、当一个RequestQueue被成功申请后会开启一个CacheDispatcher和4个默认的NetworkDispatcher。
2、CacheDispatcher缓存调度器最为第一层缓冲,开始工作后阻塞的从缓存序列mCacheQueue中取得请求;新的,过期的,或者 需要更新缓存请求,交给N个NetworkDispatcher进行处理;然后将应答交由Delivery分发者进行处理。
3、网络请求调度器NetworkDispatcher作为网络请求真实发生的地方,对消息交给BasicNetwork进行处理,同样的,将应答交由Delivery分发者进行处理。
4、Delivery根据请求所获得的应答情况做不同处理;若应答成功,则触发deliverResponse方法,最终会触发开发者为Request设定的Listener;若应答失败,则触发deliverError方法,最终会触发开发者为Request设定的ErrorListener。处理完后,一个Request的生命周期就结束了,Delivery会调用Request的finish操作,将其从mRequestQueue中移除,与此同时,如果等待列表中存在相同URL的请求,则会将剩余的层级请求全部丢入mCacheQueue交由CacheDispatcher进行处理。

Volley源码解析

标签:

原文地址:http://blog.csdn.net/u010878994/article/details/51855188

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!