Volley(四) Volley框架(从源码角度分析)

             Volley现在大家想必都已经会是用了,光使用是不够的。我们还要弄清楚Volley整个的框架,开始我也是迷迷糊糊的以为会使用就可以了,但是毕竟安卓开源的吗  咱们要学习一下大神们的思想,这样也有助于咱们更好的理解Volley

盗用别人一张Volley框架图 Volley官方也有一张图  我感觉这个比较详细就拿过来了



我们看着这个图是不是感觉很乱,其实我们可以把这张图仔细的分为三部分来看 

第一部分:main thread 主线程  (请求队列)

第二部分:cache thread 缓存线程

第三部分:网络线程




这样我们从头开始分析Volley ,我们知道Volley使用的时候第一行代码是Volley.newRequestQueue(context) 这样我们得到一个RequestQueue 我们看一下源代码

  1. public static RequestQueue newRequestQueue(Context context) {  
  2.     return newRequestQueue(context, null);  



这里我们调用了  newRequestQueue 这个方法重载 不过这个方法我们默认给第二个参数为null 好 我们看一下 带有两个参数的newRequestQueue

  1. public static RequestQueue newRequestQueue(Context context, HttpStack stack) {  
  2.     File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);  
  3.     String userAgent = "volley/0";  
  4.     try {  
  5.         String packageName = context.getPackageName();  
  6.         PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);  
  7.         userAgent = packageName + "/" + info.versionCode;  
  8.     } catch (NameNotFoundException e) {  
  9.     }  
  10.     if (stack == null) {  
  11.         if (Build.VERSION.SDK_INT >= 9) {  
  12.             stack = new HurlStack();  
  13.         } else {  
  14.             stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));  
  15.         }  
  16.     }  
  17.     Network network = new BasicNetwork(stack);  
  18.     RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);  
  19.     queue.start();  
  20.     return queue;  
  21. }  

(下面我把这段代码拆开给大家讲解一下)

我当时看到这个源码的时候  第一眼我就对第二个参数情有独钟了 因为我们知道现在大家都在讨论OkHttpClient 有的人说我用Volley  有的人说我用OkHttpClient 那么问题来了  问什么我们不用Volley+OkHttpClient呢   只要我们自己自定义我们自己的 HttpStack  就完全可以用Volley+OkHttpClient  好了 扯远了 这个问题大家自己去查资料 

好咱们看一下这个HttpStack 

  1. public interface HttpStack {  
  2.     /** 
  3.      * Performs an HTTP request with the given parameters. 
  4.      * 
  5.      * <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise, 
  6.      * and the Content-Type header is set to request.getPostBodyContentType().</p> 
  7.      * 
  8.      * @param request the request to perform 
  9.      * @param additionalHeaders additional headers to be sent together with 
  10.      *         {@link Request#getHeaders()} 
  11.      * @return the HTTP response 
  12.      */  
  13.     public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)  
  14.         throws IOException, AuthFailureError;  
  15. }  

其实就是一个接口没什么神奇的  其实这个HttpStack对象才是真正的去网络拿回我们需要的数据 (题外话 我们定义自己的HttpStack 把这个作为传输层 这样就可以使用Volley+OkHttpClient  大家有时间去研究 不扯了 我就是顺便说一句)  

好 我们不要纠结这个HttpStack  我们还是看上面这个newRequestQueue方法 我们看第10行  判断stack是不是null

  1. if (stack == null) {  
  2.         if (Build.VERSION.SDK_INT >= 9) {  
  3.             stack = new HurlStack();  
  4.         } else {  
  5.             stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));  
  6.         }  
  7.     } 


这里我们会判断手机系统版本号是不是大于9 是 创建一个HurlStack实例  不是  创建一个HttpClientStack ,其实HurlStack内部就是使用HttpURLConnection进行网络通讯的,而HttpClientStack 的内部是使用HttpClient进行通

讯的 (其实Volley最终和网络交互使用的还是HttpURLConnection和HttpClient)


 我们接着看下面几行代码


  1.  Network network = new BasicNetwork(stack);  
  2.     RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);  
  3.     queue.start();  
  4.     return queue;  

我们创建一个Network对象(NetWork也是一个接口) 在Volley中的实现类是BasicNetwork 我们可以看一下这个源码

  1. public interface Network {  
  2.     /** 
  3.      * Performs the specified request. 
  4.      * @param request Request to process 
  5.      * @return A {@link NetworkResponse} with data and caching metadata; will never be null 
  6.      * @throws VolleyError on errors 
  7.      */  
  8.     public NetworkResponse performRequest(Request<?> request) throws VolleyError;  
  9. }  


这个 Network我们稍微了解就行了 就一句话 这个NetWork的主要作用就是  根据我们传入的HttpStack对象来处理网络请求的

还是看我们上一段代码我们new了一个RequestQueue紧接着调用了它的start方法 好我们就看一下start方法

 

  1. public void start() {  
  2.     stop();  // Make sure any currently running dispatchers are stopped.  
  3.     // Create the cache dispatcher and start it.  
  4.     mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);  
  5.     mCacheDispatcher.start();  
  6.     // Create network dispatchers (and corresponding threads) up to the pool size.  
  7.     for (int i = 0; i < mDispatchers.length; i++) {  
  8.         NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,  
  9.                 mCache, mDelivery);  
  10.         mDispatchers[i] = networkDispatcher;  
  11.         networkDispatcher.start();  
  12.     }  
  13. }  


这段代码我们好好分析一下  首先我们创建了一个 CacheDispatcher(缓存线程) 然后在一个for循环里面创建了NetworkDispatcher(网络线程) 默认的会执行四次 所以我们调用Volley.newRequestQueue(context)之后默认会创建五个线程  一个缓存线程 四个网络线程    


得到RequestQueue之后 我们就得创建我们的Request了 然后调用RequestQueue的add()方法将Request传入就可以完成网络请求操作了 我们来看一下add方法的源码

  1. public <T> Request<T> add(Request<T> request) {  
  2.     // Tag the request as belonging to this queue and add it to the set of current requests.  
  3.     request.setRequestQueue(this);  
  4.     synchronized (mCurrentRequests) {  
  5.         mCurrentRequests.add(request);  
  6.     }  
  7.     // Process requests in the order they are added.  
  8.     request.setSequence(getSequenceNumber());  
  9.     request.addMarker("add-to-queue");  
  10.     // If the request is uncacheable, skip the cache queue and go straight to the network.  
  11.     if (!request.shouldCache()) {  
  12.         mNetworkQueue.add(request);  
  13.         return request;  
  14.     }  
  15.     // Insert request into stage if there's already a request with the same cache key in flight.  
  16.     synchronized (mWaitingRequests) {  
  17.         String cacheKey = request.getCacheKey();  
  18.         if (mWaitingRequests.containsKey(cacheKey)) {  
  19.             // There is already a request in flight. Queue up.  
  20.             Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);  
  21.             if (stagedRequests == null) {  
  22.                 stagedRequests = new LinkedList<Request<?>>();  
  23.             }  
  24.             stagedRequests.add(request);  
  25.             mWaitingRequests.put(cacheKey, stagedRequests);  
  26.             if (VolleyLog.DEBUG) {  
  27.                 VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);  
  28.             }  
  29.         } else {  
  30.             // Insert 'null' queue for this cacheKey, indicating there is now a request in  
  31.             // flight.  
  32.             mWaitingRequests.put(cacheKey, null);  
  33.             mCacheQueue.add(request);  
  34.         }  
  35.         return request;  
  36.     }  
  37. }  

我们直接看11行 先判断我们的Request是不是可以缓存 如果不可以缓存,直接调用mNetworkQueue.add(request); 将请求放到网络线程中   默认的是都可以缓存的 好 如果现在我们的请求都缓存起来了 现在缓存线程就可以运行了 我们直接看缓存线程里面的run方法


  1. public class CacheDispatcher extends Thread {  
  2.   
  3.     ……  
  4.   
  5.     @Override  
  6.     public void run() {  
  7.         if (DEBUG) VolleyLog.v("start new dispatcher");  
  8.         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  9.         // Make a blocking call to initialize the cache.  
  10.         mCache.initialize();  
  11.         while (true) {  
  12.             try {  
  13.                 // Get a request from the cache triage queue, blocking until  
  14.                 // at least one is available.  
  15.                 final Request<?> request = mCacheQueue.take();  
  16.                 request.addMarker("cache-queue-take");  
  17.                 // If the request has been canceled, don't bother dispatching it.  
  18.                 if (request.isCanceled()) {  
  19.                     request.finish("cache-discard-canceled");  
  20.                     continue;  
  21.                 }  
  22.                 // Attempt to retrieve this item from cache.  
  23.                 Cache.Entry entry = mCache.get(request.getCacheKey());  
  24.                 if (entry == null) {  
  25.                     request.addMarker("cache-miss");  
  26.                     // Cache miss; send off to the network dispatcher.  
  27.                     mNetworkQueue.put(request);  
  28.                     continue;  
  29.                 }  
  30.                 // If it is completely expired, just send it to the network.  
  31.                 if (entry.isExpired()) {  
  32.                     request.addMarker("cache-hit-expired");  
  33.                     request.setCacheEntry(entry);  
  34.                     mNetworkQueue.put(request);  
  35.                     continue;  
  36.                 }  
  37.                 // We have a cache hit; parse its data for delivery back to the request.  
  38.                 request.addMarker("cache-hit");  
  39.                 Response<?> response = request.parseNetworkResponse(  
  40.                         new NetworkResponse(entry.data, entry.responseHeaders));  
  41.                 request.addMarker("cache-hit-parsed");  
  42.                 if (!entry.refreshNeeded()) {  
  43.                     // Completely unexpired cache hit. Just deliver the response.  
  44.                     mDelivery.postResponse(request, response);  
  45.                 } else {  
  46.                     // Soft-expired cache hit. We can deliver the cached response,  
  47.                     // but we need to also send the request to the network for  
  48.                     // refreshing.  
  49.                     request.addMarker("cache-hit-refresh-needed");  
  50.                     request.setCacheEntry(entry);  
  51.                     // Mark the response as intermediate.  
  52.                     response.intermediate = true;  
  53.                     // Post the intermediate response back to the user and have  
  54.                     // the delivery then forward the request along to the network.  
  55.                     mDelivery.postResponse(request, response, new Runnable() {  
  56.                         @Override  
  57.                         public void run() {  
  58.                             try {  
  59.                                 mNetworkQueue.put(request);  
  60.                             } catch (InterruptedException e) {  
  61.                                 // Not much we can do about this.  
  62.                             }  
  63.                         }  
  64.                     });  
  65.                 }  
  66.             } catch (InterruptedException e) {  
  67.                 // We may have been interrupted because it was time to quit.  
  68.                 if (mQuit) {  
  69.                     return;  
  70.                 }  
  71.                 continue;  
  72.             }  
  73.         }  
  74.     }  
  75. }  



这段代码很长 我们没必要每一行都搞明白 我们看主要的地方就可以了 首先我们看到一个while循环证明我们这个线程、
、一直运行的 我们看23  24行23行我们在缓存中取出对应请求的缓存结果 如果为null 则把这个请求放到网络请求队列
我们看31行  然后我们判断这个缓存结果是不是已经过期 如果过期了 我们同样的吧Request放入到网络请求队列中如

果缓存结果不为null 并且没有过期 我们直接返回缓存的结果  39行调用 parseNetworkResponse对数据进行解析

 在往后就是回调数据了  这个方法的实现是交给Request的子类来完成的,因为不同种类的Request解析的方式也肯

定不同。 我们自定义的Request中这个方法是必须实现的 因为不同的Request解析方式不同 



整个就是缓存线程  现在我们来看一下网络线程







  1. ublic class NetworkDispatcher extends Thread {  
  2.     ……  
  3.     @Override  
  4.     public void run() {  
  5.         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  6.         Request<?> request;  
  7.         while (true) {  
  8.             try {  
  9.                 // Take a request from the queue.  
  10.                 request = mQueue.take();  
  11.             } catch (InterruptedException e) {  
  12.                 // We may have been interrupted because it was time to quit.  
  13.                 if (mQuit) {  
  14.                     return;  
  15.                 }  
  16.                 continue;  
  17.             }  
  18.             try {  
  19.                 request.addMarker("network-queue-take");  
  20.                 // If the request was cancelled already, do not perform the  
  21.                 // network request.  
  22.                 if (request.isCanceled()) {  
  23.                     request.finish("network-discard-cancelled");  
  24.                     continue;  
  25.                 }  
  26.                 addTrafficStatsTag(request);  
  27.                 // Perform the network request.  
  28.                 NetworkResponse networkResponse = mNetwork.performRequest(request);  
  29.                 request.addMarker("network-http-complete");  
  30.                 // If the server returned 304 AND we delivered a response already,  
  31.                 // we're done -- don't deliver a second identical response.  
  32.                 if (networkResponse.notModified && request.hasHadResponseDelivered()) {  
  33.                     request.finish("not-modified");  
  34.                     continue;  
  35.                 }  
  36.                 // Parse the response here on the worker thread.  
  37.                Response<?> response = request.parseNetworkResponse(networkResponse);  
  38.                 request.addMarker("network-parse-complete");  
  39.                 // Write to cache if applicable.  
  40.                 // TODO: Only update cache metadata instead of entire record for 304s.  
  41.                 if (request.shouldCache() && response.cacheEntry != null) {  
  42.                     mCache.put(request.getCacheKey(), response.cacheEntry);  
  43.                     request.addMarker("network-cache-written");  
  44.                 }  
  45.                 // Post the response back.  
  46.                 request.markDelivered();  
  47.                 mDelivery.postResponse(request, response);  
  48.             } catch (VolleyError volleyError) {  
  49.                 parseAndDeliverNetworkError(request, volleyError);  
  50.             } catch (Exception e) {  
  51.                 VolleyLog.e(e, "Unhandled exception %s", e.toString());  
  52.                 mDelivery.postError(request, new VolleyError(e));  
  53.             }  
  54.         }  
  55.     }  
  56. }  

  
现在一看头大了 这什么跟什么啊  不要急 我们没必要每一个都弄得非常清楚 我们找到里面重点(其实里面我也很多不

懂的。。。) 我们看28行 这个时候会调用performRequest 去请求网络 这个NetWork刚才我们说了这是一个接口

 Volley具体的实现是BasicNetwork  这个源码咱们就不讲了 都是一些网络请求细节方面的东西 有的不用搞太懂 理解部

分我们会使用就可以了(主要是对我这个一年多安卓的来说 源码有的不是很明白 ) 我们看37行和41行 网络请求返回给

我们一个NetworkResponse然后我们对这个对象进行解析以及缓存 刚才缓存线程已经说了  我们这个具体的解析是子

类来完成 的 因为不同的Request解析方式肯定不同  最后我们调用 postResponse进行回调我们解析的数据

 

我们看一下postResponse方法

  1. public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {  
  2.     request.markDelivered();  
  3.     request.addMarker("post-response");  
  4.     mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));  



我们调用了execute方法   这里有个参数ResponseDeliveryRunnable 我们看一下这个源码




  1. private class ResponseDeliveryRunnable implements Runnable {  
  2.     private final Request mRequest;  
  3.     private final Response mResponse;  
  4.     private final Runnable mRunnable;  
  5.   
  6.     public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {  
  7.         mRequest = request;  
  8.         mResponse = response;  
  9.         mRunnable = runnable;  
  10.     }  
  11.   
  12.     @SuppressWarnings("unchecked")  
  13.     @Override  
  14.     public void run() {  
  15.         // If this request has canceled, finish it and don't deliver.  
  16.         if (mRequest.isCanceled()) {  
  17.             mRequest.finish("canceled-at-delivery");  
  18.             return;  
  19.         }  
  20.         // Deliver a normal response or error, depending.  
  21.         if (mResponse.isSuccess()) {  
  22.             mRequest.deliverResponse(mResponse.result);  
  23.         } else {  
  24.             mRequest.deliverError(mResponse.error);  
  25.         }  
  26.         // If this is an intermediate response, add a marker, otherwise we're done  
  27.         // and the request can be finished.  
  28.         if (mResponse.intermediate) {  
  29.             mRequest.addMarker("intermediate-response");  
  30.         } else {  
  31.             mRequest.finish("done");  
  32.         }  
  33.         // If we have been provided a post-delivery runnable, run it.  
  34.         if (mRunnable != null) {  
  35.             mRunnable.run();  
  36.         }  
  37.    }  
  38. }  

我们看21行 deliverResponse这个方法是不是很熟悉 ,没错就是我们自定义Request必须实现的第二个方法 每一条

网络请求的回调都是回调到这个方法 最后我们再在这个方法中将响应的数据回调到Response.Listener的onResponse()方法中就可以了。


  

好了  整个volley框架的源码咱们就分析完了 是不是现在看晕了  我建议大家多看几遍(我当时看这个源码看了3天 


笨鸟先飞嘛。。。) 书读百遍吗 你懂得


最后我们还是总结一下吧 

我们首先在主线程中调用RequestQueue的add方法添加一条网络请求   然后我们把这条请求放到缓存队列  如果发现在

缓存中可以找到对应的请求的结果 我们就解析并且回调主线程就可以了  如果没有发现结果或者结果为null 我们就需要

把这条请求放到网络队列中 然后发送Http请求 再然后就是解析响应结果 写入缓存 并且回调主线程  

这就是一个完整的Volley请求  现在看最上面的图是不是很简单了。。。





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值