Volley源码解析

作者: Swy2w | 来源:发表于2017-05-10 22:31 被阅读27次

    前言

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

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

    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 <T> Request<T> add(Request<T> 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;
                }
            }
        }
    

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

    RequestQueue.add.png

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

    <T> void finish(Request<T> 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;
                    }
                }
            }
        }
    

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

    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有许类似,依旧上图

    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<String, String> responseHeaders = new HashMap<String, String>();  
                try {  
                    // Gather headers.  
                    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());  
                    // 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_CN.png

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

    相关文章

      网友评论

        本文标题:Volley源码解析

        本文链接:https://www.haomeiwen.com/subject/jbqntxtx.html