Volley库源码分析(上)

来源:互联网 发布:淘宝搞笑主图 编辑:程序博客网 时间:2024/06/05 08:50

整体框架

Volley使用了线程池来作为基础结构,主要分为主线程,cache线程和network线程。
主线程和cache线程都只有一个,而NetworkDispatcher线程可以有多个,这样能解决比并行问题。如下图:


核心:NetworkDispatcher

关键步骤

其中左下角是NetworkDispatcher线程,大致步骤是:
1.不断从请求队列中取出请求
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. request = mQueue.take();  
2.发起网络请求
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. NetworkResponse networkResponse = mNetwork.performRequest(request);  
3.把这个networkResponse转化为期望的数据类型,比如Response<String>,Response<Json>,Response<Bitmap>
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. Response<?> response = request.parseNetworkResponse(networkResponse);  
4.将网络响应加入缓存
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. mCache.put(request.getCacheKey(), response.cacheEntry);  
5.将网络响应发回主线程
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. mDelivery.postResponse(request, response);  
下面是NetworkDispatcher线程的主要代码:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2.     public void run() {  
  3.         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  4.         Request request;  
  5.         while (true) {  
  6.             try {  
  7.                 // Take a request from the queue.  
  8.                 request = mQueue.take();//1.从请求队列中取出一个网络请求,mQueue是BlockingQueue<Request>的实现类  
  9.             } catch (InterruptedException e) {  
  10.                 // We may have been interrupted because it was time to quit.  
  11.                 if (mQuit) {  
  12.                     return;  
  13.                 }  
  14.                 continue;  
  15.             }  
  16.   
  17.             try {  
  18.                 request.addMarker("network-queue-take");  
  19.   
  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.   
  27.                 // Tag the request (if API >= 14)  
  28.                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {  
  29.                     TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());  
  30.                 }  
  31.   
  32.                 // Perform the network request.  
  33.                 NetworkResponse networkResponse = mNetwork.performRequest(request);//2.发起网络请求  
  34.                 request.addMarker("network-http-complete");  
  35.   
  36.                 // If the server returned 304 AND we delivered a response already,  
  37.                 // we're done -- don't deliver a second identical response.  
  38.                 if (networkResponse.notModified && request.hasHadResponseDelivered()) {  
  39.                     request.finish("not-modified");  
  40.                     continue;  
  41.                 }  
  42.   
  43.                 // Parse the response here on the worker thread.  
  44.                 Response<?> response = request.parseNetworkResponse(networkResponse);//3.把这个networkResponse转化为期望的数据类型  
  45.                 request.addMarker("network-parse-complete");  
  46.   
  47.                 // Write to cache if applicable.  
  48.                 // TODO: Only update cache metadata instead of entire record for 304s.  
  49.                 if (request.shouldCache() && response.cacheEntry != null) {  
  50.                     mCache.put(request.getCacheKey(), response.cacheEntry);//4.将网络响应加入缓存  
  51.                     request.addMarker("network-cache-written");  
  52.                 }  
  53.   
  54.                 // Post the response back.  
  55.                 request.markDelivered();  
  56.                 mDelivery.postResponse(request, response);//5.将网络响应发回主线程  
  57.             } catch (VolleyError volleyError) {  
  58.                 parseAndDeliverNetworkError(request, volleyError);  
  59.             } catch (Exception e) {  
  60.                 VolleyLog.e(e, "Unhandled exception %s", e.toString());  
  61.                 mDelivery.postError(request, new VolleyError(e));  
  62.             }  
  63.         }  
  64.     }  

如何实现多线程下载

要理解Volley的并行性实现,必需理解PriorityBlockingQueue并发类。注意到我们在NetworkDispatcher循环中并没有使用显式的同步(使用Lock或者使用synchronize),因为PriorityBlockingQueue的实现是线程安全的。在使用显式的wait()和notifyAll()时存在的类和类之间的耦合可以因此消除,因为每个类只和BlockingQueue通信。这个知识点可以参考《Java编程思想》P713.
BlockingQueue implementations are thread-safe. All queuing methods achieve their effects atomically using internal locks or other forms of concurrency control. 
所以可以有多个NetworkDispatcher线程同时从请求队列PriorityBlockingQueue中取出请求而不会产生线程冲突,那就是Volley支持多线程下载图片的方式。

实际上,BlockingQueue接口是用于解决生产者-消费者问题的。
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class Producer implements Runnable {  
  2.    private final BlockingQueue queue;  
  3.    Producer(BlockingQueue q) { queue = q; }  
  4.    public void run() {  
  5.      try {  
  6.        while (true) { queue.put(produce()); }  
  7.      } catch (InterruptedException ex) { ... handle ...}  
  8.    }  
  9.    Object produce() { ... }  
  10.  }  
  11.   
  12.  class Consumer implements Runnable {  
  13.    private final BlockingQueue queue;  
  14.    Consumer(BlockingQueue q) { queue = q; }  
  15.    public void run() {  
  16.      try {  
  17.        while (true) { consume(queue.take()); }  
  18.      } catch (InterruptedException ex) { ... handle ...}  
  19.    }  
  20.    void consume(Object x) { ... }  
  21.  }  
  22.   
  23.  class Setup {  
  24.    void main() {  
  25.      BlockingQueue q = new SomeQueueImplementation();  
  26.      Producer p = new Producer(q);  
  27.      Consumer c1 = new Consumer(q);  
  28.      Consumer c2 = new Consumer(q);  
  29.      new Thread(p).start();  
  30.      new Thread(c1).start();  
  31.      new Thread(c2).start();  
  32.    }  
  33.  }}  
所以在Volley中,request就是产品,主线程是生产者,NetworkDispatcher线程是消费者。

另外注意到,NetworkDispatcher的实现其实是策略设计模式:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** The queue of requests to service. */  
  2.     private final BlockingQueue<Request> mQueue;  
  3.     /** The network interface for processing requests. */  
  4.     private final Network mNetwork;  
  5.     /** The cache to write to. */  
  6.     private final Cache mCache;  
  7.     /** For posting responses and errors. */  
  8.     private final ResponseDelivery mDelivery;  
  9.     /** Used for telling us to die. */  
  10.     private volatile boolean mQuit = false;  
  11.   
  12.     /** 
  13.      * Creates a new network dispatcher thread.  You must call {@link #start()} 
  14.      * in order to begin processing. 
  15.      * 
  16.      * @param queue Queue of incoming requests for triage 
  17.      * @param network Network interface to use for performing requests 
  18.      * @param cache Cache interface to use for writing responses to cache 
  19.      * @param delivery Delivery interface to use for posting responses 
  20.      */  
  21.     public NetworkDispatcher(BlockingQueue<Request> queue,  
  22.             Network network, Cache cache,  
  23.             ResponseDelivery delivery) {  
  24.         mQueue = queue;  
  25.         mNetwork = network;  
  26.         mCache = cache;  
  27.         mDelivery = delivery;  
  28.     }  
NetworkDispatcher构造函数的几个参数都是接口,而run方法则使用这些策略类方法实现了算法的主体流程,具体实现有些留给了开发者,有些则是框架实现。比如ImageCache作为一级缓存的Cache方法留给了开发者实现,由开发者控制具体的缓存策略,当然Volley建议我们使用LRUCache作为L1缓存的实现。

最后,NetworkDispatcher的数组则构成了RequestQueue类中线程池,由RequestQueue统一启动和停止:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.      * Creates the worker pool. Processing will not begin until {@link #start()} is called. 
  3.      * 
  4.      * @param cache A Cache to use for persisting responses to disk 
  5.      * @param network A Network interface for performing HTTP requests 
  6.      * @param threadPoolSize Number of network dispatcher threads to create 
  7.      * @param delivery A ResponseDelivery interface for posting responses and errors 
  8.      */  
  9.     public RequestQueue(Cache cache, Network network, int threadPoolSize,  
  10.             ResponseDelivery delivery) {  
  11.         mCache = cache;  
  12.         mNetwork = network;  
  13.         mDispatchers = new NetworkDispatcher[threadPoolSize];  
  14.         mDelivery = delivery;  
  15.     }  
  16.       
  17.     /** 
  18.      * Starts the dispatchers in this queue. 
  19.      */  
  20.     public void start() {  
  21.         stop();  // Make sure any currently running dispatchers are stopped.  
  22.         // Create the cache dispatcher and start it.  
  23.         mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);  
  24.         mCacheDispatcher.start();  
  25.   
  26.         // Create network dispatchers (and corresponding threads) up to the pool size.  
  27.         for (int i = 0; i < mDispatchers.length; i++) {  
  28.             NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,  
  29.                     mCache, mDelivery);  
  30.             mDispatchers[i] = networkDispatcher;  
  31.             networkDispatcher.start();  
  32.         }  
  33.     }  


如何将网络响应发回主线程

通过PriorityBlockingQueue将各个线程解耦之后,最终的结果都必需直接发回主线程,这里用到的方法就是安卓中非常常见的Handler+Looper机制。
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. mDelivery.postResponse(request, response);  
mDelivery是ResponseDelivery接口,由ExecutorDelivery实现,以下是ExecutorDelivery中postResponse的实现:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2.     public void postResponse(Request<?> request, Response<?> response) {  
  3.         postResponse(request, response, null);  
  4.     }  
  5.   
  6.     @Override  
  7.     public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {  
  8.         request.markDelivered();  
  9.         request.addMarker("post-response");  
  10.         mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));  
  11.     }  
我们看看mResponsePoster.execute()是怎么实现的:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** Used for posting responses, typically to the main thread. */  
  2.    private final Executor mResponsePoster;  
  3.   
  4.    /** 
  5.     * Creates a new response delivery interface. 
  6.     * @param handler {@link Handler} to post responses on 
  7.     */  
  8.    public ExecutorDelivery(final Handler handler) {  
  9.        // Make an Executor that just wraps the handler.  
  10.        mResponsePoster = new Executor() {  
  11.            @Override  
  12.            public void execute(Runnable command) {  
  13.                handler.post(command);  
  14.            }  
  15.        };  
  16.    }  
没错,就是通过handler去post请求,因为要将请求发回主线程,所以这里的Handler就是主线程的Hanlder
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public RequestQueue(Cache cache, Network network, int threadPoolSize) {  
  2.        this(cache, network, threadPoolSize,  
  3.                new ExecutorDelivery(new Handler(Looper.getMainLooper())));  
0 0
原创粉丝点击