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

来源:互联网 发布:员工积分管理系统源码 编辑:程序博客网 时间:2024/05/12 11:13

             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请求  现在看最上面的图是不是很简单了。。。





0 0
原创粉丝点击