Volley源码解析

前言

很早之前就写过关于Volley的文章,但是那时候对源码的厉害不不够深刻,所有没有在文章中作出源码的分析,最近又重新继续撸羊毛一样看了一篇,许多问题顿时清楚了许多,虽然你可能会说现在流行的Android网络框架有多种,okhhtp,Retrofit等等,但是作为Google官方推荐的网络框架,还是值的我们去深思学习的。

这篇文章我主要是解析源码对于具体的使用我就不做过多的说明了,如果你也想阅读源码可以从官网下载或者直接在你的编辑器中查看

Volley解剖

1、流程图

Volley源码解析_第1张图片
volley.png

上面是Google关于Volley的官网上给出的图片,如果看到这种图你觉得一头雾水的话,那你可以先大概了记忆一下,接下来我们围绕这张图一步步看个究竟。

2、源码解析

Volley.java

我们使用Volley的第一步都是先获取一个RequestQueue对象,那么首当其冲我们先从它入手。我们进入到Volley类

public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, (HttpStack)null);
    }

只是调用了newRequestQueue的方法重载并且第二个参数传入null,我们在看看这个方法(其实Volley.class有几个具体的newRequestQueue方法,不过基本都是互相调用的)

public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        return newRequestQueue(context, stack, -1);
    }

同样的调用newRequestQueue的方法重载,再进去看看

public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
        File cacheDir = new File(context.getCacheDir(), "volley");
        String userAgent = "volley/0";
        try {
            String network = context.getPackageName();
            PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
            userAgent = network + "/" + queue.versionCode;
        } catch (NameNotFoundException var7) {
            ;
        }
        //判断stack是否为空 并根据版本号创建不同的Http对象
        if(stack == null) {
            if(VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }
        //通过stack够着一个处理网络请求的对象
        BasicNetwork network1 = new BasicNetwork((HttpStack)stack);
        RequestQueue queue1; 
        //构建RequestQueue 并启动和还回它
        if(maxDiskCacheBytes <= -1) {
            queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
        } else {
            queue1 = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network1);
        }

        queue1.start();
        return queue1;
    }

找到具体实现的方法, 可以发现,其中通过if语句判断stack是否等于null,如果为null则创建一个对象,这个对象在API Level>=9的时候创建基于HttpURLConnection 的 HurlStack,如果API Level<9,则采用基于 HttpClient 的 HttpClientStack创建

创建好HttpStack后,一用这个satck构造了一个BasicNetwork对象用来处理网络请求,并且构造一个代表缓存(Cache)的基于Disk的DiskBasedCache对象,最后将网络(network1)对象和缓存(Cache)对象传入构建一个 RequestQueue,启动这个 RequestQueue,并返回它。

关于HttpURLConnection 和 AndroidHttpClient(HttpClient 的封装)如何选择及原因可以查看这篇文章

RequestQueue.java

大致理清怎么创建RequestQueue后,我们再开看看RequestQueue的start()方法是怎么一回事

private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
public void start() {
        this.stop();//停止现在正在运行的cache和network调度线程
        //创建一个CacheDispatcher实例
        this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
        this.mCacheDispatcher.start();
        //创建NetworkDispatcher实例 length默认为4
        for(int i = 0; i < this.mDispatchers.length; ++i) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
            this.mDispatchers[i] = networkDispatcher;
            //启动网络调度线程
            networkDispatcher.start();
        }

    }

首先先停止现在正在运行的调度线程,然后创建一个
CacheDispatcher实例,接着在for循环里创建NetworkDispatcher并且调用他们的start()方法,默认情况for循环会执行四次。也就是说当调用了Volley.newRequestQueue(context)之后,就会有五个线程一直在后台运行。

得到了RequestQueue后,我们需要构将我们想要的Request(或者我们自定义的Request),然后调用RequestQueue的add()方法将Request传入就可以完成网络请求操作了,那么必须的我们要去add()方法的内部看看

public  Request add(Request request) {
        request.setRequestQueue(this);
        Set var2 = this.mCurrentRequests;
        //添加到当前队列mCurrentRequests中
        synchronized(this.mCurrentRequests) {
            this.mCurrentRequests.add(request);
        }
        //为请求添加序列号
        request.setSequence(this.getSequenceNumber());
        request.addMarker("add-to-queue");
        //判断是否允许被缓存 默认允许
        if(!request.shouldCache()) {
            //不允许缓存则直接添加到网络队列中
            this.mNetworkQueue.add(request);
            return request;
        } else {
            Map var8 = this.mWaitingRequests;
            synchronized(this.mWaitingRequests) {
                String cacheKey = request.getCacheKey();
                //判断是否有相同请求在等待队列中
                if(this.mWaitingRequests.containsKey(cacheKey)) {
                    Object stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);
                    if(stagedRequests == null) {
                        stagedRequests = new LinkedList();
                    }
  
                    ((Queue)stagedRequests).add(request);
                    //加入到相同请求的等待队列中
                    this.mWaitingRequests.put(cacheKey, stagedRequests);
                    if(VolleyLog.DEBUG) {
                        VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[]{cacheKey});
                    }
                } else {
                    //添加到等待队列中,并且加入到缓存队列中
                    this.mWaitingRequests.put(cacheKey, (Object)null);
                    this.mCacheQueue.add(request);
                }

                return request;
            }
        }
    }

具体的步骤我大致在代码中给出了解释,文字容易看的眼花撩日,我特意写出了流程图:

Volley源码解析_第2张图片
RequestQueue.add.png

接下来再来看看请求完成后的finish方法

 void finish(Request request) {
        Set var2 = this.mCurrentRequests;
        //从正在请求集合中移除request
        synchronized(this.mCurrentRequests) {
            this.mCurrentRequests.remove(request);
        }

        List var10 = this.mFinishedListeners;
        synchronized(this.mFinishedListeners) {
            Iterator cacheKey = this.mFinishedListeners.iterator();

            while(true) {
                if(!cacheKey.hasNext()) {
                    break;
                }

                RequestQueue.RequestFinishedListener waitingRequests = (RequestQueue.RequestFinishedListener)cacheKey.next();
                waitingRequests.onRequestFinished(request);
            }
        }
        //request是否允许缓存
        if(request.shouldCache()) {
            Map var11 = this.mWaitingRequests;
            synchronized(this.mWaitingRequests) {
                String cacheKey1 = request.getCacheKey();
                //将request从等待队列移除
                Queue waitingRequests1 = (Queue)this.mWaitingRequests.remove(cacheKey1);
                if(waitingRequests1 != null) {
                    if(VolleyLog.DEBUG) {
                        VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", new Object[]{Integer.valueOf(waitingRequests1.size()), cacheKey1});
                    }
                    //将等待队列所有的请求添加到缓存请求队列中
                    this.mCacheQueue.addAll(waitingRequests1);
                }
            }
        }

    }

注释在代码中给出,这里就不作过多的解释了。



了解完一次用法的具体实现后,我们知道除了RequestQueue,Request,ResponseDelivery似乎最重要的就是线程调度了。那我们奋起直追再来看看缓存线程调度和网络线程调度

CacheDispatcher.java

先来看看缓存线程调度,既然说是线程调度,那我们知道CacheDispatcher肯定是继承线程的,结果发果然CacheDispatcher是集成与Thread。我们看看它主要的方法run()方法

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

        // 初始化缓存
        mCache.initialize();
        //无限循环
        while (true) {
            try {
                // 从缓存队列mCacheQueue中获取一个Request
                // at least one is available.
                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
                    mNetworkQueue.put(request);
                    continue;
                }

                // 缓存结果是否过期
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    // 过期则加入到网络队列mNetworkQueue
                    mNetworkQueue.put(request);
                    continue;
                }

                //解析缓存结果为Response
                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;
                }
            }
        }
    }

代码很多,还是老方法,上我做的流程图一目了然。

Volley源码解析_第3张图片
CacheDispatcher.png

NetworkDispatcher.java

接下来再看看NetworkDispatcher的主要方法run()方法

public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            Request request;
            try {
                // 从网络队列获取一个Request
                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);

                // 获取NetworkResponse
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");

                // If the server returned 304 AND we delivered a response already,
                // performRequest方法通过304响应 并且请求有传输响应
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

                // 解析数据成Respons
                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");
                }

                // Post the response back.
                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);
            }
        }
    }

和CacheDispatcher有许类似,依旧上图

Volley源码解析_第4张图片
NetworkDispatcher.png

其中要注意的是mNetwork.performRequest(request)方法只有在得到304响应才算成功。这里的Network其实具体实现就是我们在newRequestQueue中k看到的BasicNetwork,我们来看下它的performRequest()方法

public class BasicNetwork implements Network {  
    ……省略
    @Override  
    public NetworkResponse performRequest(Request request) throws VolleyError {  
        long requestStart = SystemClock.elapsedRealtime();  
        while (true) {  
            HttpResponse httpResponse = null;  
            byte[] responseContents = null;  
            Map responseHeaders = new HashMap();  
            try {  
                // Gather headers.  
                Map headers = new HashMap();  
                addCacheHeaders(headers, request.getCacheEntry());  
                httpResponse = mHttpStack.performRequest(request, headers);  
                StatusLine statusLine = httpResponse.getStatusLine();  
                int statusCode = statusLine.getStatusCode();  
                responseHeaders = convertHeaders(httpResponse.getAllHeaders());  
                // Handle cache validation.  
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {  
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,  
                            request.getCacheEntry() == null ? null : request.getCacheEntry().data,  
                            responseHeaders, true);  
                }  
                // Some responses such as 204s do not have content.  We must check.  
                if (httpResponse.getEntity() != null) {  
                  responseContents = entityToBytes(httpResponse.getEntity());  
                } else {  
                  // Add 0 byte response as a way of honestly representing a  
                  // no-content request.  
                  responseContents = new byte[0];  
                }  
                // if the request is slow, log it.  
                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);  
            } catch (Exception e) {  
              ……省略
            }  
        }  
    }  
}  

打多是一些网络请求码的处理,不做详细讲解。

最后注意在解析完了NetworkResponse中的数据之后,又会调用ExecutorDelivery的postResponse()方法来回调解析出的数据,代码如下所示:

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()方法中的代码是什么样的:

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()) {  
            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();  
        }  
   }  
}  

代码虽然不多,但我们并不需要行行阅读,抓住重点看即可。其中在第22行调用了Request的deliverResponse()方法,而这个方法就是我们在自定义Request时需要重写的另外一个方法,每一条网络请求的响应都是回调到这个方法中,最后我们再在这个方法中将响应的数据回调到Response.Listener的onResponse()方法中就可以了。

长叹一口气,到这里我们总算把Volley的执行流程基本梳理了一遍,是不是已经感觉已经思路很清晰了呢?对了,还记得在文章一开始的那张流程图吗,刚才还不能理解,现在回头看看是不是明白了呢。下面贴出一张网络上的中文图,相信你已经明白了

Volley源码解析_第5张图片
Volley_CN.png

如果有什么错误,还请不舍赐教。

你可能感兴趣的:(Volley源码解析)