Volley框架源码浅析(二)

尊重原创 http://write.blog.csdn.net/postedit/25921795

在前面的一片文章Volley框架浅析(一)中我们知道在RequestQueue这个类中,有两个队列:本地队列和网络队列

/** The cache triage queue. */
    private final PriorityBlockingQueue<Request<?>> mCacheQueue =
        new PriorityBlockingQueue<Request<?>>();

    /** The queue of requests that are actually going out to the network. */
    private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
        new PriorityBlockingQueue<Request<?>>();

与之对应的分别有本地线程和网络线程,通过对RequestQueue源码的分析,本地线程有一条,而网络线程默认有四条,我们可以对网络线程的个数进行设置,我们首先来学习一下本地线程:

(1) CacheDispatcher.java

public class CacheDispatcher extends Thread {

    private static final boolean DEBUG = VolleyLog.DEBUG;

	//本地队列,从RequestQueue中传递进来的
    private final BlockingQueue<Request<?>> mCacheQueue;

    //网络请求队列,也是从RequestQueue中传递进来,当本地缓存没有命中时,需要把请求从本地队列加入网络队列
    private final BlockingQueue<Request<?>> mNetworkQueue;

    //磁盘缓存对象
    private final Cache mCache;

    //就是用于从子线程向Ui线程发送数据
    private final ResponseDelivery mDelivery;

    /** Used for telling us to die. */
    private volatile boolean mQuit = false;

    /**
     * Creates a new cache triage dispatcher thread.  You must call {@link #start()}
     * in order to begin processing.
     *
     * @param cacheQueue Queue of incoming requests for triage
     * @param networkQueue Queue to post requests that require network to
     * @param cache Cache interface to use for resolution
     * @param delivery Delivery interface to use for posting responses
     */
    public CacheDispatcher(
            BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,
            Cache cache, ResponseDelivery delivery) {
        mCacheQueue = cacheQueue;
        mNetworkQueue = networkQueue;
        mCache = cache;
        mDelivery = delivery;
    }

    /**
     * Forces this dispatcher to quit immediately.  If any requests are still in
     * the queue, they are not guaranteed to be processed.
     */
    public void quit() {
        mQuit = true;
        interrupt();
    }

    @Override
    public void run() {
        if (DEBUG) VolleyLog.v("start new dispatcher");
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        // 缓存初始化,将磁盘中的数据读入内存
        mCache.initialize();

        while (true) {
            try {

                // 阻塞式从队列中取出请求
                final Request<?> request = mCacheQueue.take();
                request.addMarker("cache-queue-take");

                // 判断request是否被取消了(调用cancel方法),如果取消了就不执行,再次到队列中取请求
                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()) {
                    // Completely unexpired cache hit. Just deliver the response.
					//命中,并且不需要刷新
                    mDelivery.postResponse(request, response);
                } else {
					//命中,需要刷新,将请求放入网络队列,这里面的代码其实可以根据需求自己重写
                    // Soft-expired cache hit. We can deliver the cached response,
                    // but we need to also send the request to the network for
                    // refreshing.
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);

                    // Mark the response as intermediate.
                    response.intermediate = true;

                    // Post the intermediate response back to the user and have
                    // the delivery then forward the request along to the network.
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {
                                // Not much we can do about this.
                            }
                        }
                    });
                }

            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }
        }
    }
}

(2) NetworkDispatcher.java

public class NetworkDispatcher extends Thread {
    /** 网络队列 */
    private final BlockingQueue<Request<?>> mQueue;
    /** 用于Http请求,根据前面的学习,他其实使用的是HttpURLConnection或者HttpClient. */
    private final Network mNetwork;
    /** 本地缓存,网络请求成功后,放入缓存. */
    private final Cache mCache;
    /** For posting responses and errors. */
    private final ResponseDelivery mDelivery;
    /** Used for telling us to die. */
    private volatile boolean mQuit = false;

    /**
     * Creates a new network dispatcher thread.  You must call {@link #start()}
     * in order to begin processing.
     *
     * @param queue Queue of incoming requests for triage
     * @param network Network interface to use for performing requests
     * @param cache Cache interface to use for writing responses to cache
     * @param delivery Delivery interface to use for posting responses
     */
    public NetworkDispatcher(BlockingQueue<Request<?>> queue,
            Network network, Cache cache,
            ResponseDelivery delivery) {
        mQueue = queue;
        mNetwork = network;
        mCache = cache;
        mDelivery = delivery;
    }

    /**
     * Forces this dispatcher to quit immediately.  If any requests are still in
     * the queue, they are not guaranteed to be processed.
     */
    public void quit() {
        mQuit = true;
        interrupt();
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    private void addTrafficStatsTag(Request<?> request) {
        // Tag the request (if API >= 14)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
        }
    }

    @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        Request<?> request;
        while (true) {
            try {
                // 从队列中阻塞式取出一个请求.
                request = mQueue.take();
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }

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

                // 同理需要判断是否取消,如果取消执行下一个请求
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }

                addTrafficStatsTag(request);

                // 通过NetWork的perfromRequest方法放回一个NetworkResponse对象
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");

                // 如果这个返回结果已经发送到了ui线程,就将它finish
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

                // 将NetworkResponse 解析成Response.
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

                // 如果需要缓存,那么将结果存入缓存
                if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

                // 标记为已经发送
                request.markDelivered();
				//将数据发送到Ui线程
                mDelivery.postResponse(request, response);
            } catch (VolleyError volleyError) {
                parseAndDeliverNetworkError(request, volleyError);
            } catch (Exception e) {
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
                mDelivery.postError(request, new VolleyError(e));
            }
        }
    }

    private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
        error = request.parseNetworkError(error);
        mDelivery.postError(request, error);
    }
}

通过上面的代码,我们来总结一下一个请求的执行过程吧:

1、一个请求就是一个Request对象,首先将Request对象加入到RequestQueue中.

2、判断Request是否可以缓存,如果可以,则加入到本地缓存队列,否则加入网络队列

3、本地线程不断监听本地队列是否有请求,如果有请求取出来

4、判断Request是否取消,如果取消,处理下一个请求

5、判断缓存是否命中,如果没有命中,将该请求加入网络队列

6、如果命中,但是过期,同样将该请求加入网络队列

7、如果命中,并且不用刷新,那么直接放回结果,不用加入网络队列

8、如果命中,并且需要刷新,那么放回结果,并且加入网络队列

9、同样4条网络线程也在不断监听网络队列是否有请求,一旦发现有请求,取出请求,判断是否取消,如果取消,那么取出下一个请求

10、如果没有取消,那么通过NetWork进行http请求,将请求结果封装成NetworkResponse,然后转换为Response

11、如果可以缓存,那么将数据写入缓存

12、通过Delivery将Response返回到ui线程

通过以上12步,完成了一个完整的请求

研究了这么久,我们还没有研究Request和Response是什么呢,如果熟悉http请求的同学相信很好理解,

Request就是一个http请求,Response就是http返回的内容,先看看Request这个类吧

Request是一个抽象类,我只介绍比较重要的几个方法:

public abstract class Request<T> implements Comparable<Request<T>> {
	//Http 请求方法 POST,GET
    private final int mMethod;

    /** 请求URL*/
    private final String mUrl;
	//用于出错时的回调接口
    private final Response.ErrorListener mErrorListener;

    /** 这个请求在队列中的顺序 */
    private Integer mSequence;

   ...

    /** 是否可以缓存 */
    private boolean mShouldCache = true;

    /** 是否已经取消了,网络线程和本地线程都会对此判断,如果取消了就不请求了 */
    private boolean mCanceled = false;

    /** 请求策略,比如设置最大重试次数之类的*/
    private RetryPolicy mRetryPolicy;

    /**
     * Creates a new request with the given method (one of the values from {@link Method}),
     * URL, and error listener.  Note that the normal response listener is not provided here as
     * delivery of responses is provided by subclasses, who have a better idea of how to deliver
     * an already-parsed response.
     */
    public Request(int method, String url, Response.ErrorListener listener) {
        mMethod = method;
        mUrl = url;
        mErrorListener = listener;
        setRetryPolicy(new DefaultRetryPolicy());

        mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
    }

    /**
     * Sets the retry policy for this request.
     *
     * @return This Request object to allow for chaining.
     */
    public Request<?> setRetryPolicy(RetryPolicy retryPolicy) {
        mRetryPolicy = retryPolicy;
        return this;
    }

   ...

    /**
     * 通过此方法取消一个请求
     */
    public void cancel() {
        mCanceled = true;
    }

    /**
     * 判断是否已经取消.
     */
    public boolean isCanceled() {
        return mCanceled;
    }

    /**
     * 获取请求头
     * @throws AuthFailureError In the event of auth failure
     */
    public Map<String, String> getHeaders() throws AuthFailureError {
        return Collections.emptyMap();
    }

    /**
     * Returns a Map of POST parameters to be used for this request, or null if
     * a simple GET should be used.  Can throw {@link AuthFailureError} as
     * authentication may be required to provide these values.
     *
     * <p>Note that only one of getPostParams() and getPostBody() can return a non-null
     * value.</p>
     * @throws AuthFailureError In the event of auth failure
     *
     * @deprecated Use {@link #getParams()} instead.
     */
    @Deprecated
    protected Map<String, String> getPostParams() throws AuthFailureError {
        return getParams();
    }

    /**
     * Returns a Map of parameters to be used for a POST or PUT request.  Can throw
     * {@link AuthFailureError} as authentication may be required to provide these values.
     *
     * <p>Note that you can directly override {@link #getBody()} for custom data.</p>
     *
     * @throws AuthFailureError in the event of auth failure
     */
    protected Map<String, String> getParams() throws AuthFailureError {
        return null;
    }

    public String getBodyContentType() {
        return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
    }

    /**
     * 设置能否缓存
     *
     * @return This Request object to allow for chaining.
     */
    public final Request<?> setShouldCache(boolean shouldCache) {
        mShouldCache = shouldCache;
        return this;
    }

    /**
     * 判断是否能够缓存
     */
    public final boolean shouldCache() {
        return mShouldCache;
    }

    /**
     * 这是个抽象方法,我们必须实现,用于将NetworkResponse 转化为Response
     * @param response Response from the network
     * @return The parsed response, or null in the case of an error
     */
    abstract protected Response<T> parseNetworkResponse(NetworkResponse response);

    /**
     * 这个我们也必须实现,用于将Response发送到ui线程
     * @param response The parsed response returned by
     * {@link #parseNetworkResponse(NetworkResponse)}
     */
    abstract protected void deliverResponse(T response);

}

下面继续看看Response这个类:
public class Response<T> {

    /** 成功的时候回调. */
    public interface Listener<T> {
        /** Called when a response is received. */
        public void onResponse(T response);
    }

    /** 失败的时候回调 */
    public interface ErrorListener {
        /**
         * Callback method that an error has been occurred with the
         * provided error code and optional user-readable message.
         */
        public void onErrorResponse(VolleyError error);
    }

    /** 成功的时候创建一个Response. */
    public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
        return new Response<T>(result, cacheEntry);
    }

    /**
     * 失败的时候创建一个Response
     */
    public static <T> Response<T> error(VolleyError error) {
        return new Response<T>(error);
    }

    /** Parsed response, or null in the case of error. */
    public final T result;

    /**
     * Returns whether this response is considered successful.
     */
    public boolean isSuccess() {
        return error == null;
    }

	//私有的,我们无法调用
    private Response(T result, Cache.Entry cacheEntry) {
        this.result = result;
        this.cacheEntry = cacheEntry;
        this.error = null;
    }

    private Response(VolleyError error) {
        this.result = null;
        this.cacheEntry = null;
        this.error = error;
    }
}

学习了上面两个类后,我们需要知道如下知识:

Volley中的任何请求都是继承Request的,如Volley提供的StringRequest,JsonArrayRequest,JsonObjectRequest

ImageRequest等等,并且要实现其中的两个方法

abstract protected Response<T> parseNetworkResponse(NetworkResponse response);

abstract protected void deliverResponse(T response);

T是泛型,StringRequest中T表示String,后期我将会简单介绍这几种Request的使用,敬请大家期待。。。

最后在介绍一个接口,就是ResponseDelivery.java

它的一个实现类是ExecutorDelivery.java

public class ExecutorDelivery implements ResponseDelivery {
    /** 执行已提交的 Runnable 任务的对象。此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法,在线程池中经常用到 */
    private final Executor mResponsePoster;

    /**
     * 传入一个Handler,其实就是运行在主线的Handler,我想你应该明白为什么他能够从子线程
	 将数据传入ui线程了
     * @param handler {@link Handler} to post responses on
     */
    public ExecutorDelivery(final Handler handler) {
        // Make an Executor that just wraps the handler.
        mResponsePoster = new Executor() {
            @Override
            public void execute(Runnable command) {
				//这里调用了handler的post方法
                handler.post(command);
            }
        };
    }

    /**
     * Creates a new response delivery interface, mockable version
     * for testing.
     * @param executor For running delivery tasks
     */
    public ExecutorDelivery(Executor executor) {
        mResponsePoster = executor;
    }

    @Override
    public void postResponse(Request<?> request, Response<?> response) {
        postResponse(request, response, null);
    }

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

    /**
     * A Runnable used for delivering network responses to a listener on the
     * main thread.
     */
    @SuppressWarnings("rawtypes")
    private class ResponseDeliveryRunnable implements Runnable {
        private final Request mRequest;
        private final Response mResponse;
        private final Runnable mRunnable;

        public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
            mRequest = request;
            mResponse = response;
            mRunnable = runnable;
        }

        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            // If this request has canceled, finish it and don't deliver.
            if (mRequest.isCanceled()) {
                mRequest.finish("canceled-at-delivery");
                return;
            }

            // Deliver a normal response or error, depending.
            if (mResponse.isSuccess()) {
				//在这里调用了deliverResponse
                mRequest.deliverResponse(mResponse.result);
            } else {
                mRequest.deliverError(mResponse.error);
            }

            // If this is an intermediate response, add a marker, otherwise we're done
            // and the request can be finished.
            if (mResponse.intermediate) {
                mRequest.addMarker("intermediate-response");
            } else {
                mRequest.finish("done");
            }

            // If we have been provided a post-delivery runnable, run it.
            if (mRunnable != null) {
                mRunnable.run();
            }
       }
    }
}

好了,今天就写到这里吧,大家有什么不明白的欢迎留言讨论....

Volley框架源码浅析(二),布布扣,bubuko.com

时间: 2024-08-02 06:58:04

Volley框架源码浅析(二)的相关文章

Android网络通信Volley框架源码浅析(三)

尊重原创 http://write.blog.csdn.net/postedit/26002961 通过前面浅析(一)和浅析(二)的分析,相信大家对于Volley有了初步的认识,但是如果想更深入的理解,还需要靠大家多多看源码. 这篇文章中我们主要来研究一下使用Volley框架请求大量图片的原理,在Android的应用中,通过http请求获取的数据主要有三类: 1.json 2.xml 3.Image 其中json和xml的获取其实原理很简单,使用Volley获取感觉有点大财小用了,了解Volle

Volley框架源码浅析(一)

尊重原创http://blog.csdn.net/yuanzeyao/article/details/25837897 从今天开始,我打算为大家呈现关于Volley框架的源码分析的文章,Volley框架是Google在2013年发布的,主要用于实现频繁而且粒度比较细小的Http请求,在此之前Android中进行Http请求通常是使用HttpUrlConnection和HttpClient进行,但是使用起来非常麻烦,而且效率比较地下,我想谷歌正式基于此种原因发布了Volley框架,其实出了Voll

(干货) Android Volley框架源码详细解析

前言 经常接触Android网络编程的我们,对于Volley肯定不陌生,但我们不禁要问,对于Volley我们真的很了解吗?Volley的内部是怎样实现的?为什么几行代码就能快速搭建好一个网络请求?我们不但要知其然,也要知其所以然,抱着这样的目的,本文主要详细讲述Volley的源码,对内部流程进行详细解析. Part 1.从RequestQueue说起 (1)还记得搭建请求的第一步是什么吗?是新建一个请求队列,比如说这样: RequestQueue queue = Volley.newReques

Android之Volley框架源码分析

临近毕业,各种事情各种忙.我也没有认真专注写过博客,最近仔细看了Volley框架的使用及其源码,思前想后,想挑战一下自己,还是写一篇博客来分享,如有错误,欢迎吐槽. Volley简介 网络请求是一个App很重要的一部分,android系统只是提供了一个平台,而android应用则是基于这个平台上进行展示数据,起到与用户进行交互的作用,数据来源于服务端,而二者之间必须通过互联网进行传输数据,在Android系统发布初期,很多开发者都是在Apache协会的Http协议的基础上进行网络请求方法的封装,

Volley框架源码分析

Volley框架分析Github链接 Volley框架分析 Volley源码解析 为了学习Volley的网络框架,我在AS中将Volley代码重新撸了一遍,感觉这种照抄代码也是一种挺好的学习方式.再分析Volley源码之前,我们先考虑一下,如果我们自己要设计一个网络请求框架,需要实现哪些事情,有哪些注意事项? 我的总结如下: 需要抽象出request请求类(包括url, params, method等),抽象出request请求类之后,我们可以对其继承从而实现丰富的扩展功能. 需要抽象出resp

Bottle 框架源码学习 二

上一篇简单分析了route的基本用法 本篇分析一下run函数的运行原理 def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,         interval=1, reloader=False, quiet=False, plugins=None,         debug=None, **kargs):          if NORUN: return     if reloader and not os.env

Volley框架源码修改,添加头部验证Hreaders问题

应项目要求修改网络问题,为了让项目更加健壮,使用Volley框架,这个android 推荐使用的网络框架,整体来说请求的方式网上都可以查的到,这里就不说,现在就说说添加头部验证,因为我们的项目在请求每一个链接的时候都会验证,所以添加头部是一个十分必要的情况.网上说让在请求里边添加getHeaders()方法,方法是确实能用,但是是有前提的. 主要是使用不同的队列的问题: (1)Volley.newRequestQueue(this) 如果是调用这个方法的话,那么在执行StringRequest方

Google Volley框架源码走读

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 阅读前一篇<Google Volley使用之自定义> http://blog.csdn.net/yanbober/article/details/45307099 开源项目链接 Volley自定义 Android Developer文档 Volley主页:https://android.g

Volley源码分析二

在前两天我发布的文章:Volley源码分析一 中我较为详细的分析了Volley,今天继续,这篇文章会讲一些上一篇没有提到的比较细节的点,以及对于Volley源码中一些可以优化的实现的一些思考 ByteArrayPool的分析 byte[] 的回收池,用于 byte[] 的回收再利用,减少了内存的分配和回收.主要通过一个元素长度从小到大排序的ArrayList作为 byte[] 的缓存,另有一个按使用时间先后排序的ArrayList属性用于缓存满时清理元素. public synchronized