Volley源码解析

抽时间看了下Volley的源码,感觉这个框架还是很不错的,这里对其源码进行分析。

GitHub链接

主要从以下几个方面分析:
a. 简要介绍 Volley 的使用
b. Volley 访问网络的整体流程
c. 对源码中一些重要的类的总结

简要介绍 Volley 的使用

 // 创建请求队列
    RequestQueue mQueue = Volley.newRequestQueue(context);    // 创建请求
    StringRequest stringRequest = new StringRequest("http://www.baidu.com",  
                        new Response.Listener<String>() {  
                            @Override  
                            public void onResponse(String response) {  
                                Log.d("TAG", response);  
                            }  
                        }, new Response.ErrorListener() {  
                            @Override  
                            public void onErrorResponse(VolleyError error) {  
                                Log.e("TAG", error.getMessage(), error);  
                            }  
                        });  
 
    // 添加请求到队列中
    mQueue.add(stringRequest);

Volley 访问网络的整体流程

首先通过 Volley#newRequestQueue() 创建请求队列。
在Volley类中,一共就提供了两个静态的重载方法 newRequestQueue()
主要看下 newRequestQueue(Context, HttpStack)

   public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, null);
    }
    
    public static RequestQueue newRequestQueue(Context context, HttpStack stack){
        return newRequestQueue(context, stack, -1);
    }
    //最终调用这个方法
    //context 用于获取disk缓存地址
    //HttpStack 用于network , null for default
    //maxDiskCacheBytes  最大disk缓存大小  。-1 for default size 是5M
    public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
       //默认内部缓存路径 data/data/packagename/cache/ volley/文件夹
        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);//volley

        String userAgent = "volley/0";
        try {
            String packageName = context.getPackageName();
            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
           //请求默认userAgent 
            userAgent = packageName + "/" + info.versionCode;
        } catch (NameNotFoundException e) {
        }

        if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                //使用默认HttpStack,这个类的作用是是用httpUrlConnection请求网络;
                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));
            }
        }
        //使用httpstack构造 Network对象    。封装网络请求的一些操作
        Network network = new BasicNetwork(stack);
        
        RequestQueue queue;
        if (maxDiskCacheBytes <= -1)
        {
            // No maximum size specified
               //使用Network对象和缓存路径,构造 RequestQueue 请求队列对象。 创建请求队列并启动
            queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        }
        else
        {
            // Disk cache size specified
            queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
        }
       //请求队列运行
        queue.stcart();

        return queue;
    }

可以看到,其中主要创建了NetworkHttpStackRequestQueue,并启动了RequestQueue
最终网络访问就封装在HttpStack中,这个后面再看

先看一下请求队列类 RequestQueue

/**
 * A request dispatch queue with a thread pool of dispatchers.
 *
 * Calling {@link #add(Request)} will enqueue the given Request for dispatch,
 * resolving from either cache or network on a worker thread, and then delivering
 * a parsed response on the main thread.
 */
//带有调度线程池 的 请求调度队列 。
//通过add 方法添加请求到队列中等待调度,在一个工作线程中要么缓存要么请求网络,然后发送已经解析的响应给主线程
public class RequestQueue {

    /** 请求完成回调接口 */
    public static interface RequestFinishedListener<T> {
        /** 请求执完了回调接口. */
        public void onRequestFinished(Request<T> request);
    }

    /**
     * Staging area for requests that already have a duplicate request in flight.
     */
   //HashMap形式的缓存队列,对于已经没有执行完/正在执行的相同的请求,被放进到等待请求队列中。
    private final Map<String, Queue<Request<?>>> mWaitingRequests =
            new HashMap<String, Queue<Request<?>>>();

    /**
     * The set of all requests currently being processed by this RequestQueue. A Request
     * will be in this set if it is waiting in any queue or currently being processed by
     * any dispatcher.
     */
   //当前请求队列正在执行的请求集合 CurrentRequest
    private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();

    /** The cache triage queue. */
   //请求缓存队列 使用的是阻塞队列的形式,用来缓存请求的,下面那个Cache是缓存响应的
    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<?>>();

    /** Number of network request dispatcher threads to start. */
  //默认网络调度线程的数量是4个
    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;

    /** Cache interface for retrieving and storing responses. */
   //文件缓存对象,获取并存储响应用的文件缓存对象
    private final Cache mCache;

    /** Network interface for performing requests. */
   //真正访问网络的 Network对象
    private final Network mNetwork;

    /** Response delivery mechanism. */
   //响应发送机制
    private final ResponseDelivery mDelivery;

    /** The network dispatchers. */
   //网络调度线程 默认是4个
    private NetworkDispatcher[] mDispatchers;

    /** The cache dispatcher. */
   //缓存调度线程 
    private CacheDispatcher mCacheDispatcher;
   //请求执行完成回调接口集合
    private List<RequestFinishedListener> mFinishedListeners =
            new ArrayList<RequestFinishedListener>();
  //请求队列构造函数  其他几个构造函数就不说了
    public RequestQueue(Cache cache, Network network, int threadPoolSize,
            ResponseDelivery delivery) {
        mCache = cache;
        mNetwork = network;
        mDispatchers = new NetworkDispatcher[threadPoolSize];
        mDelivery = delivery;
    }

    /**
     * Starts the dispatchers in this queue.
     */
   //请求队列启动这个队列  这个start函数主要是初始化缓存调度线程和网络请求线程
    public void start() {
        stop();  // Make sure any currently running dispatchers are stopped.
        // Create the cache dispatcher and start it
       //实例化缓存调度线程
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
      //开启缓存调度线程
        mCacheDispatcher.start();

        // Create network dispatchers (and corresponding threads) up to the pool size.
       //初始化网络调度线程 并开启网络调度线程
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

    /**
     * Gets a sequence number.
     */
   //获取序列号
    public int getSequenceNumber() {
        return mSequenceGenerator.incrementAndGet();
    }

    /**
     * Gets the {@link Cache} instance being used.
     */
    public Cache getCache() {
        return mCache;
    }

    /**
     * Adds a Request to the dispatch queue.
     * @param request The request to service
     * @return The passed-in request
     */
    在构建了请求队列后,发起请求只要把请求添加到请求队列中就好了,接下来看下RequestQueue#add()代
   码    这里挑一些主要的代码看
    public <T> Request<T> add(Request<T> request) {
        // Tag the request as belonging to this queue and add it to the set of current requests.
        //给这个请求打上属于这个队列的ta,并且把它添加到当前队列中
        request.setRequestQueue(this);
        synchronized (mCurrentRequests) {
            1,添加在当前正在执行的集合中去
            mCurrentRequests.add(request);
        }

        // Process requests in the order they are added.
        2, 设置这个请求的序列号,即给每个请求一个唯一号
        request.setSequence(getSequenceNumber());
        request.addMarker("add-to-queue");

        // If the request is uncacheable, skip the cache queue and go straight to the network.
        3, 判断这个请求是否可缓存,如果不可缓存直接添加到Network队列
        if (!request.shouldCache()) {
         
            mNetworkQueue.add(request);
            return request;
        }

        // Insert request into stage if there's already a request with the same cache key in flight.
       如果有相同的请求正在执行但是没有返回结果,就把当前的这个请求加入到等待请求的HashMap中
        synchronized (mWaitingRequests) {
            请求中得到这个缓存的key
            String cacheKey = request.getCacheKey();
           看看mWaitingRequests的map中有没有同一个key的请求队列
            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<?>>();
                }
                如果有同一个cachekey的队列,就加进去
                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.
                如果没有同一个cachekey的的request队列,就插入null,添加到  “缓存请求阻塞队列”中,说明有个新的请求,被缓存了
                mWaitingRequests.put(cacheKey, null);
                mCacheQueue.add(request);
            }
            return request;
        }
    }

从上面的RequestQueue类中可以看出这个类干了什么活。
在成员变量上
1, final Map<String, Queue<Request<?>>> mWaitingRequests = new HashMap<String, Queue<Request<?>>>();请求等待Map,当同一个CacheKey的request正在执行/没有返回结果,就把同一个key的Request放进这个请求等待Map。
2,final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<?>>()
缓存请求的阻塞队列 。
3,final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?>>() 真正发送出去的网络请求队列
4 , final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>()当前正在执行的Request的集合。当add请求时,直接将Request加入到这个集合中,在判断这个Request是否可缓存
5,final Cache mCache 响应的硬盘缓存
6,final Network mNetwork正真访问网络的接口,其实现类封装了HttpUrlConnection的访问
7,final ResponseDelivery mDelivery 发送响应到主线程的句柄
8,NetworkDispatcher[] mDispatchers网络调度线程
9,CacheDispatcher mCacheDispatcher缓存调度线程

缓存调度线程和网络调度线程在start函数中传递构造对象是 传递是4个参数是mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);,缓存请求阻塞队列mCacheQueue,网络请求阻塞队列mNetworkQueue,响应硬盘缓存mCache,发送响应的句柄mDelivery,都是final修饰的,是说明这两个线程中,必定对这几个变量做着某种操作,目前猜测。

在方法上,主要是start()方法和add()方法
1, start方法,实例化缓存调度线程,CacheDispatcher,网络调度线程NetworkDispatcher
2, add方法,先将Request直接加入 “当前正在执行的Request的集合中mCurrentRequest”中,再为此Request加入唯一的序列号,在判断此Request是否可以缓存,不可缓存直接加入"网络请求阻塞队列mNetworkQueue"中,可以缓存,如果waitingRequest中存在相同Key的Request正在被执行,就加入到waitingRequest中,如果不存在就加入“缓存请求阻塞队列" cacheQueue中,请求需要缓存的话,会加入到缓存队列中,交给缓存派发器处理,否则交给网络请求派发器处理

到目前为止,Volley.newRequestQueue()方法完成了,即我们的网络请求第一步,建立请求队列完成。

先小结一下:建立请求队列所做的工作是,创建文件缓存(默认),实例化BasicNetwork,实例化Delivery用于发送线程请求,创建一条缓存线程和四条网络请求线程(默认)并运行。

网络请求Request的实现原理

在创建完请求队列后,接着就是建立一个请求,请求的方式可以是StringRequest、JsonArrayRequest或者ImageRequest等,那么这些请求的背后原理是什么呢?我们拿最简单的StringRequest来说,它继承自Request,而Request则是所有请求的父类,所以说如果你要自定义一个网络请求,就应该继承自Request。接下来我们看看StringRequest的源码,因为不管Request的子类是什么,大体的实现思路都是一致的,所以我们弄懂了StringRequest,那么对于其他的请求类的理解是相通的

public class StringRequest extends Request<String> {
    private Listener<String> mListener;
    public StringRequest(int method, String url, Listener<String> listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }

    /**
     * Creates a new GET request.
     *
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }

    @Override
    protected void onFinish() {
        super.onFinish();
        mListener = null;
    }
    发送最终的响应到回调接口
    @Override
    protected void deliverResponse(String response) {
        if (mListener != null) {
            mListener.onResponse(response);
        }
    }
     解析网络响应的方法
    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException e) {
            parsed = new String(response.data);
        }
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }
}

我们主要关注的是deliverResponse方法和parseNetworkResponse。可以看出,这两个方法都是重写的,我们翻看父类Request的对应方法,发现是抽象方法,说明这两个方法在每一个自定义的Request中都必须重写。这里简单说说这两个方法的作用。
先看deliverResponse方法:它内部调用了mListener.onResponse(response)方法,而这个方法正是我们在写一个请求的时候,添加的listener所重写的onResponse方法,也就是说,响应成功后在这里调用了onResponse()方法。
接着看pareNetworkResponse方法:可以看出这里主要是对response响应做出一些处理。可以对比一下不同请求类的这个方法,都会不同的,所以说,这个方法是针对不同的请求类型而对响应做出不同的处理。比如说,如果是StringRequest,则将响应包装成String类型;如果是JsonObjectRequest,则将响应包装成JsonObject。那么现在应该清楚了:对于想要得到某一种特殊类型的请求,我们可以自定义一个Request,重写这两个方法即可。

小结一下:Request类做的工作主要是初始化一些参数,比如说请求类型请求的url错误的回调方法;而它的任一子类重写deliverResponse方法来实现成功的回调重写parseNetworkResponse()方法来处理响应数据;至此,一个完整的Request请求搭建完毕。

添加请求

前面已经完成了请求队列的创建,Request请求的创建,那么接下来就是把请求添加进队列了。我们看RequestQueue#add()源码

/**
 * Adds a Request to the dispatch queue.
 * @param request The request to service
 * @return The passed-in request
 */
public <T> Request<T> add(Request<T> request) {
    // Tag the request as belonging to this queue and add it to the set of current requests.
    标记当前请求,表示这个请求由当前RequestQueue处理
    request.setRequestQueue(this);
    synchronized (mCurrentRequests) {
        mCurrentRequests.add(request);
    }
    // Process requests in the order they are added.
    设置当前请求的序号
    request.setSequence(getSequenceNumber());
    request.addMarker("add-to-queue");
    // If the request is uncacheable, skip the cache queue and go straight to the network.
    如果请求不能缓存,直接添加到 “网络请求队列”mNetworkQueue ,默认是可以缓存
    if (!request.shouldCache()) {
        mNetworkQueue.add(request);
        return request;
    }

    // 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.
            如果有相同请求正在处理,那么把这个请求放进mWaitingRequest中,等待。
            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中,同时也放进“缓存请求队列 ”mCacheQueue中
            这代表这个请求已经开始在 缓存线程 中运行了
            mWaitingRequests.put(cacheKey, null);
            mCacheQueue.add(request);
        }
        return request;
    }
}

得出如下结论:在这个add方法中,主要判断一个Request请求是否可以缓存(默认是可以缓存的),如果不可以则直接添加到网络请求队列mNetworkQueue,开始网络通信;如果可以,则进一步判断当前是否有相同的请求正在进行如果有相同的请求,则让这个请求放进mWaitingRequest中,排队等待,如果没有相同的请求,则直接放进 缓存请求队列 mCacheQueue中,就交给缓存调度线程处理了
可以看一下add流程图,来自网络:

image.png

缓存调度线程 和 网络调度线程

通过上面的解释,目前请求队列RequestQueue和Request已经构建好了,接下类就该看看具体的网络请求是怎么发出的,怎么访问的,以及请求的响应是怎么回调到主线程的

缓存调度线程
在RequestQueue的start方法中实例化了缓存线程并开始运行,一直处于等待状态,而上面把请求添加进了缓存线程,此时缓存线程就开始真正的工作了。我们来看缓存线程的源码,主要看它的run()方法,CacheDispatcher#run():

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

    // Make a blocking call to initialize the cache.
    mCache.initialize();

    Request<?> request;
    死循环
    while (true) {
        // release previous request object to avoid leaking request object when mQueue is drained.
        request = null;
        try {
            // Take a request from the queue.
            从缓存队列中取出请求
            request = mCacheQueue.take();
        } ...
        try {
            request.addMarker("cache-queue-take");

            // If the request has been canceled, don't bother dispatching it.
            if (request.isCanceled()) {
                request.finish("cache-discard-canceled");
                continue;
            }

            // Attempt to retrieve this item from cache.
            从文件缓存中取出这个请求的响应结果
            Cache.Entry entry = mCache.get(request.getCacheKey());
            if (entry == null) {
                request.addMarker("cache-miss");
                // Cache miss; send off to the network dispatcher.
                响应结果是丢失,就计入网络请求队列,交给网络请求调度线程NetworkDispacter处理
                mNetworkQueue.put(request);
                continue;
            }

            // If it is completely expired, just send it to the network.
            判断缓存是否过期,如果过期,,在加入网络请求队列,交给网络调度线程NetworkDispacter处理
            if (entry.isExpired()) {
                request.addMarker("cache-hit-expired");
                request.setCacheEntry(entry);
                重新加入网络请求对列,交给网络请求调度线程NetworkDispacter处理
                mNetworkQueue.put(request);
                continue;
            }

            // We have a cache hit; parse its data for delivery back to the request.
          如果文件缓存的响应,没有过期,封装成NetworkResponse,返回Request的解析网络响应的方法parseNetworkResponse
            request.addMarker("cache-hit");
            先将响应的结果包装成NetworkResponse,然后调用Request子类的
            parseNetworkResponse方法解析数据
            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.
                调用ExecutorDelivey#postResponse方法
               异步把响应response 发送到request中
                mDelivery.postResponse(request, response);
            } else {
                ....
            }
        } catch (Exception e) {
            VolleyLog.e(e, "Unhandled exception %s", e.toString());
        }
    }
}

while(true)循环,表示它一直在等待缓存队列的新请求的出现。
接着,先判断这个请求是否有对应的缓存结果,如果没有则直接添加到网络请求队列;
接着,再判断这个缓存结果是否过期了,如果过期则同样地添加到网络请求队列;
接下来便是对缓存结果的处理了,我们可以看到,先是把缓存结果包装成NetworkResponse类,然后调用了Request的parseNetworkResponse,这个方法我们在part2说过,子类需要重写这个方法来处理响应数据。最后,把处理好的数据post到主线程,这里用到了ExecutorDelivery#postResponse()方法,下面会分析到。
  小结CacheDispatcher线程主要对请求的响应进行,判断是否已经有缓存响应,是否已经过期,根据需要放进网络请求队列。同时对相应结果进行包装、处理,然后交由ExecutorDelivery处理。

这里以一张流程图显示它的完整工作流程:


image.png
网络请求线程

上面提到,请求不能缓存、缓存结果不存在、缓存过期的时候会把请求添加进请求队列,此时一直等待的网络请求线程由于获取到请求,终于要开始工作了,我们来看NetworkDispatcher#run()方法

@Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        Request<?> request;
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            // release previous request object to avoid leaking request object when mQueue is drained.
            request = null;
            try {
                // Take a request from the queue.
               从队列中取Request
                request = mQueue.take();
            } ...

            try {
                ...
                // Perform the network request.
                调用BasicNetwork实现类进行网络请求,并获得响应①
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                ...

                // Parse the response here on the worker thread.
                对响应进行解析
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");
                ...
                request.markDelivered();
               发送响应到主线程
                mDelivery.postResponse(request, response);

                // Write to cache if applicable.
                将响应结果写进缓存
                if (request.shouldCache() && response.cacheEntry != null) {    
                   mCache.put(request.getCacheKey(), response.cacheEntry);   
                   request.addMarker("network-cache-written");
                }
            } 
        }
    }

源码做了适当的删减,大体上和CacheDispatcher的逻辑相同,这里关注①号代码,这里调用了BasicNetwork#perfromRequest()方法,把请求传递进去,可以猜测,这个方法内部实现了网络请求的相关操作,那么我们进去看看,

@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 {
                // Gather headers.
                Map<String, String> headers = new HashMap<String, String>();
                addCacheHeaders(headers, request.getCacheEntry());

                 mHttpStack执行request

                httpResponse = mHttpStack.performRequest(request, headers);  // 1
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();

                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                // Handle cache validation.
                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);  // 2
                    }

                    // A HTTP 304 response does not have all header fields. We
                    // have to use the header fields from the cache entry plus
                    // the new ones from the response.
                    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
                    entry.responseHeaders.putAll(responseHeaders);
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                            entry.responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart);
                }       
                ...
        }
    }

主要看①号代码,mHttpStack.performRequest();这里调用了mHttpStack的performRequest方法,那么mHttpStack是什么呢?我们可以翻上去看看RequestQueue实例化BasicNetwork的时候传递的stack值,该值就是根据不同的系统版本号而实例化的HttpStack对象(版本号大于9的是HurlStack,小于9的是HttpClientStack),由此可知,这里实际调用的是HurlStack.performRequest()方法,

方法的内部基本是关于HttpUrlConnection的逻辑代码,这里就不展开说了。

可以这么说:HurlStack封装好了HttpUrlConnection,而HttpClientStack封装了HttpClient。该方法返回了httpResponse,接着把这个响应交由②处处理,封装成NetworkResponse对象并返回。在NetworkDispatcher#run()方法获取返回的NetworkResponse对象后,对响应解析,通过ExecutorDelivery#postResponse()方法回调解析出来的数据,这个过程和CacheDispatcher相同。

ExecutorDelivery 通知主线程

在CacheDispatcher和NetworkDispatcher中最后都有调用到ExecutorDelivery#postResponse()方法,那么这个方法到底是做什么呢?由于缓存线程和网络请求线程都不是主线程,所以主线程需要有“人”通知它网络请求已经完成了,而这个“人”正是由ExecutorDelivery充当。在完成请求后,通过ExecutorDelivery#postResponse()方法,最终会回调到主线程中重写的onResponse()方法。我们看看这个方法的源码ExecutorDelivery#postResponse():

@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));
    }

在方法内部调用了mResponsePoster#execute()方法,那么,这个mResponsePoster是在哪里来的呢?其实这个成员变量是在ExecutorDelivery实例化的时候同时实例化的,而ExecutorDelivery则是在RequestQueue实例化的时候同时实例化的,读者可以自行查看相应的构造方法,其实这些工作在par 1建立请求队列的时候已经全部做好了。接着我们可以看以下代码,ExecutorDelivery#ExecutorDelivery():

public ExecutorDelivery(final Handler handler) {
        // Make an Executor that just wraps the handler.
        实例化Executor,并且重写execute方法
        mResponsePoster = new Executor() {
            @Override
            public void execute(Runnable command) {
                这里获取的handler是主线程的handler
                handler.post(command);
            }
        };
    }

execute()方法接收一个Runnable对象,那么我们回到上面的

mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));

可以看出这里实例化了一个ResponseDeliveryRunnable对象作为Runnable对象。而这里的ResponseDeliveryRunnable则是当前类Executor的一个内部类,实现了Runnable接口,我们来看看:

/**
     * A Runnable used for delivering network responses to a listener on the
     * main thread.
     */
   被用来发送网络到主线程的runnable
    @SuppressWarnings("rawtypes")
    private class ResponseDeliveryRunnable implements Runnable {
        private final Request mRequest;
        private final Response mResponse;
        private final Runnable mRunnable;

        //构造方法
        ...
        @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()) {
                调用request的deliverResponse方法
                mRequest.deliverResponse(mResponse.result);  // 1
            } else {
                mRequest.deliverError(mResponse.error);
            }
            ...
            }
       }
    }

总结

用google官方的一幅图来说明以上所说各个部分的关系(图片来自网络):


image.png

Volley的优缺点

优点

自动的调度网络请求
多并发的网络请求
可以缓存http请求
可以缓存响应
支持取消请求的API,可以取消单个请求,可以设置取消请求的范围域。

缺点

使用的是httpclient、HttpURLConnection
6.0不支持httpclient了,如果想支持得添加org.apache.http.legacy.jar
非常不适合大的文件流操作,例如上传和下载。因为Volley会把所有的服务器端返回的数据在解析期间缓存进内存。
只支持http请求
图片加载性能一般

参考链接:
https://www.jianshu.com/p/15e6209d2e6f
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2017/0605/8040.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,980评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,422评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,130评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,553评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,408评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,326评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,720评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,373评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,678评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,722评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,486评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,335评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,738评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,283评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,692评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,893评论 2 335

推荐阅读更多精彩内容