【源码分析】Volley 用法及源码分析

学了这么久的Android开发,总算到了这一天。网络框架。


工欲善其事,必先利其器。

写一个框架,之前先看volley究竟是怎么写的。

看volley之前,先学会如何使用。ok,来看看如何使用。

一、Volley使用:

1)网络请求代码如下:

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. RequestQueue mQueue = Volley.newRequestQueue(this);  
  2.        StringRequest stringRequest = new StringRequest(“http://182.61.37.49:8080/spring-mvc-study/hello/login”,  
  3.                new Response.Listener<String>() {  
  4.                    @Override  
  5.                    public void onResponse(String response) {  
  6.                        Log.d(“TAG”, response);  
  7.                    }  
  8.                }, new Response.ErrorListener() {  
  9.            @Override  
  10.            public void onErrorResponse(VolleyError error) {  
  11.                Log.e(“TAG”, error.getMessage(), error);  
  12.            }  
  13.        });  
  14.        mQueue.add(stringRequest);  

《【源码分析】Volley 用法及源码分析》

	RequestQueue mQueue = Volley.newRequestQueue(this);
        StringRequest stringRequest = new StringRequest("http://182.61.37.49:8080/spring-mvc-study/hello/login",
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        Log.d("TAG", response);
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.e("TAG", error.getMessage(), error);
            }
        });
        mQueue.add(stringRequest);

2)看看我写的demo的效果:

《【源码分析】Volley 用法及源码分析》

二.阅读源码

读前辈读烂的源码最简单的方法:先把代码书写逻辑弄懂。打个比方,就好像读书的时候写作文,先知道个中心思想大纲,再做平铺扩充。(参考自郭神博客

《一》中心思想

写写关于我自己的理解: 

  • 先画一个思路总体图。

《【源码分析】Volley 用法及源码分析》

  • 按标注说明:

1.维护一个“请求队列”;

2.向队列中添加请求(4.队列添加请求的具体方法);

3.队列执行请求(5.缓存分发线程;6.网络分发线程);

  • 总结各个对象间关系说明:

Volley初始化维护了一个请求队列(包含缓存队列,网络请求队列两种)。该队列包含两种线程,一是缓存分发线程,二是网络请求分发线程。见名知意,缓存分发线程用来从缓存队列取出缓存(请求)分发到网络请求队列,网络请求分发线程用来将网络请求队列的数据(请求)取出并通过网络请求得到返回结果。

  • 流程:

当网络请求进来,添加进队列,队列按顺序执行请求,获得返回结果。

  • 核心:

该队列包含两种线程,一是缓存分发线程,二是网络请求分发线程。缓存分发线程用来从缓存队列取出缓存(请求)分发到网络请求队列,网络请求分发线程用来将网络请求队列的数据(请求)取出并通过网络请求得到返回结果。

《二》核心代码对应:

1)初始化4个对象

先初始化缓存队列、网络请求队列,然后初始化缓存分发器并start,网络请求分发器并start,按照如下先后顺序执行。

  • 缓存队列

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. /** The cache triage queue. */  
  2.     private final PriorityBlockingQueue<Request<?>> mCacheQueue =  
  3.         new PriorityBlockingQueue<Request<?>>();  

《【源码分析】Volley 用法及源码分析》

/** The cache triage queue. */
    private final PriorityBlockingQueue<Request<?>> mCacheQueue =
        new PriorityBlockingQueue<Request<?>>();
  • 网络请求队列

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. /** The queue of requests that are actually going out to the network. */  
  2.     private final PriorityBlockingQueue<Request<?>> mNetworkQueue =  
  3.         new PriorityBlockingQueue<Request<?>>();  

《【源码分析】Volley 用法及源码分析》

/** The queue of requests that are actually going out to the network. */
    private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
        new PriorityBlockingQueue<Request<?>>();
  • 缓存分发器(Thread)

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);  

《【源码分析】Volley 用法及源码分析》

mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
  • 网络请求分发器(Thread)

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,  
  2.                     mCache, mDelivery);  

《【源码分析】Volley 用法及源码分析》

NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);

2)当添加请求,分几种情况。

[java]
view plain
copy
print
?

  1. public <T> Request<T> add(Request<T> request) {  
  2.         …  
  3.         // If the request is uncacheable, skip the cache queue and go straight to the network.  
  4.         if (!request.shouldCache()) {  
  5.             mNetworkQueue.add(request);  
  6.             return request;  
  7.         }  
  8.         …  
  9.     }  

《【源码分析】Volley 用法及源码分析》

public <T> Request<T> add(Request<T> request) {
        ...
        // If the request is uncacheable, skip the cache queue and go straight to the network.
        if (!request.shouldCache()) {
            mNetworkQueue.add(request);
            return request;
        }
        ...
    }

先判断
是否需要缓存
,如果不需要直接加入网络请求队列,over。

如果需要缓存,则判断是否等待请求Map中是否有该请求的缓存

如果有,则获取到请求缓存链表,添加该请求到缓存链表。并更新等待请求Map。

如果没有,则在等待请求Map中添加该请求缓存key(即URL),缓存队列添加该请求。

[java]
view plain
copy
print
?

  1. if (mWaitingRequests.containsKey(cacheKey)) {  
  2.                 // There is already a request in flight. Queue up.  
  3.                 Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);  
  4.                 if (stagedRequests == null) {  
  5.                     stagedRequests = new LinkedList<Request<?>>();  
  6.                 }  
  7.                 stagedRequests.add(request);  
  8.                 mWaitingRequests.put(cacheKey, stagedRequests);  
  9.                 if (VolleyLog.DEBUG) {  
  10.                     VolleyLog.v(“Request for cacheKey=%s is in flight, putting on hold.”, cacheKey);  
  11.                 }  
  12.             } else {  
  13.                 // Insert ‘null’ queue for this cacheKey, indicating there is now a request in  
  14.                 // flight.  
  15.                 mWaitingRequests.put(cacheKey, null);  
  16.                 mCacheQueue.add(request);  
  17.             }  

《【源码分析】Volley 用法及源码分析》

if (mWaitingRequests.containsKey(cacheKey)) {
                // There is already a request in flight. Queue up.
                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
                if (stagedRequests == null) {
                    stagedRequests = new LinkedList<Request<?>>();
                }
                stagedRequests.add(request);
                mWaitingRequests.put(cacheKey, stagedRequests);
                if (VolleyLog.DEBUG) {
                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
                }
            } else {
                // Insert 'null' queue for this cacheKey, indicating there is now a request in
                // flight.
                mWaitingRequests.put(cacheKey, null);
                mCacheQueue.add(request);
            }

3)执行请求逻辑(核心)

缓存分发线程CacheDispatcher执行请求:

整体代码附上:(不用看,后紧跟着具体分析)

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

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

《【源码分析】Volley 用法及源码分析》

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

        // Make a blocking call to initialize the cache.
        mCache.initialize();

        while (true) {
            try {
                // Get a request from the cache triage queue, blocking until
                // at least one is available.
                final Request<?> request = mCacheQueue.take();
                request.addMarker("cache-queue-take");

                // If the request has been canceled, don't bother dispatching it.
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }

                // Attempt to retrieve this item from cache.
                Cache.Entry entry = mCache.get(request.getCacheKey());
                if (entry == null) {
                    request.addMarker("cache-miss");
                    // Cache miss; send off to the network dispatcher.
                    mNetworkQueue.put(request);
                    continue;
                }

                // If it is completely expired, just send it to the network.
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                }

                // We have a cache hit; parse its data for delivery back to the request.
                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;
                }
                continue;
            }
        }
    }

<1>取本地缓存结果,如果本地缓存结果为空,则将请求添加到网络请求队列。

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. // Attempt to retrieve this item from cache.  
  2.                 Cache.Entry entry = mCache.get(request.getCacheKey());  
  3.                 if (entry == null) {  
  4.                     request.addMarker(“cache-miss”);  
  5.                     // Cache miss; send off to the network dispatcher.  
  6.                     mNetworkQueue.put(request);  
  7.                     continue;  
  8.                 }  

《【源码分析】Volley 用法及源码分析》

// Attempt to retrieve this item from cache.
                Cache.Entry entry = mCache.get(request.getCacheKey());
                if (entry == null) {
                    request.addMarker("cache-miss");
                    // Cache miss; send off to the network dispatcher.
                    mNetworkQueue.put(request);
                    continue;
                }

<2>如果结果已经过期,则将请求添加到网络请求队列。

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. // If it is completely expired, just send it to the network.  
  2.                 if (entry.isExpired()) {  
  3.                     request.addMarker(“cache-hit-expired”);  
  4.                     request.setCacheEntry(entry);  
  5.                     mNetworkQueue.put(request);  
  6.                     continue;  
  7.                 }  

《【源码分析】Volley 用法及源码分析》

// If it is completely expired, just send it to the network.
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                }

<3>如果结果存在,并且不过期,则通过请求解析结果

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. // We have a cache hit; parse its data for delivery back to the request.  
  2.                 request.addMarker(“cache-hit”);  
  3.                 Response<?> response = request.parseNetworkResponse(  
  4.                         new NetworkResponse(entry.data, entry.responseHeaders));  
  5.                 request.addMarker(“cache-hit-parsed”);  

《【源码分析】Volley 用法及源码分析》

// We have a cache hit; parse its data for delivery back to the request.
                request.addMarker("cache-hit");
                Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                request.addMarker("cache-hit-parsed");

<4>如果结果不需要刷新,将结果发送给response,否则添加到网络请求队列。(网络请求具体见后续分析)(response返回见后续分析)

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. if (!entry.refreshNeeded()) {  
  2.                     // Completely unexpired cache hit. Just deliver the response.  
  3.                     mDelivery.postResponse(request, response);  
  4.                 } else {  
  5.                     // Soft-expired cache hit. We can deliver the cached response,  
  6.                     // but we need to also send the request to the network for  
  7.                     // refreshing.  
  8.                     request.addMarker(“cache-hit-refresh-needed”);  
  9.                     request.setCacheEntry(entry);  
  10.   
  11.                     // Mark the response as intermediate.  
  12.                     response.intermediate = true;  
  13.   
  14.                     // Post the intermediate response back to the user and have  
  15.                     // the delivery then forward the request along to the network.  
  16.                     mDelivery.postResponse(request, response, new Runnable() {  
  17.                         @Override  
  18.                         public void run() {  
  19.                             try {  
  20.                                 mNetworkQueue.put(request);  
  21.                             } catch (InterruptedException e) {  
  22.                                 // Not much we can do about this.  
  23.                             }  
  24.                         }  
  25.                     });  
  26.                 }  

《【源码分析】Volley 用法及源码分析》

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.
                            }
                        }
                    });
                }

网络分发线程NetworkDispatcher执行请求:

整体代码如下:(不用细看,后紧跟着具体分析)

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

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

《【源码分析】Volley 用法及源码分析》

@Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            Request<?> request;
            try {
                // Take a request from the queue.
                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 the request was cancelled already, do not perform the
                // network request.
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }

                addTrafficStatsTag(request);

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

                // If the server returned 304 AND we delivered a response already,
                // we're done -- don't deliver a second identical response.
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

                // Parse the response here on the worker thread.
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

                // Write to cache if applicable.
                // TODO: Only update cache metadata instead of entire record for 304s.
                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);
            }
        }
    }

1)从网络请求队列取请求

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. request = mQueue.take();  

《【源码分析】Volley 用法及源码分析》

request = mQueue.take();

2)执行网络请求并解析网络请求数据。(网络请求具体见后续分析)

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. // Perform the network request.  
  2.                 NetworkResponse networkResponse = mNetwork.performRequest(request);  

《【源码分析】Volley 用法及源码分析》

// Perform the network request.
                NetworkResponse networkResponse = mNetwork.performRequest(request);

其中:解析response代码:

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. // Parse the response here on the worker thread.  
  2.                 Response<?> response = request.parseNetworkResponse(networkResponse);  

《【源码分析】Volley 用法及源码分析》

// Parse the response here on the worker thread.
                Response<?> response = request.parseNetworkResponse(networkResponse);

3)将请求结果networkResponse存入缓存

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. if (request.shouldCache() && response.cacheEntry != null) {  
  2.                     mCache.put(request.getCacheKey(), response.cacheEntry);  
  3.                     request.addMarker(“network-cache-written”);  
  4.                 }  

《【源码分析】Volley 用法及源码分析》

if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

4)将结果返回(response返回见后续分析)

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. mDelivery.postResponse(request, response);  

《【源码分析】Volley 用法及源码分析》

mDelivery.postResponse(request, response);

4.网络请求

思路总体图中的内容分析差不多了。然后把网络请求部分具体说下。请求网络的时候,我们使用了如下代码:

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. mNetwork.performRequest(request)  

《【源码分析】Volley 用法及源码分析》

mNetwork.performRequest(request)

mNetwork是什么?见Volley初始化代码newRequestQueue中:层层传递,mNetwork即network。

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. Network network = new BasicNetwork(stack);  

《【源码分析】Volley 用法及源码分析》

Network network = new BasicNetwork(stack);

找到BasicNetwork的performRequest方法:

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. @Override  
  2.     public NetworkResponse performRequest(Request<?> request) throws VolleyError {  
  3.         long requestStart = SystemClock.elapsedRealtime();  
  4.         while (true) {  
  5.             HttpResponse httpResponse = null;  
  6.             byte[] responseContents = null;  
  7.             Map<String, String> responseHeaders = Collections.emptyMap();  
  8.             try {  
  9.                 // Gather headers.  
  10.                 Map<String, String> headers = new HashMap<String, String>();  
  11.                 addCacheHeaders(headers, request.getCacheEntry());  
  12.                 httpResponse = mHttpStack.performRequest(request, headers);  
  13.                 StatusLine statusLine = httpResponse.getStatusLine();  
  14.                 int statusCode = statusLine.getStatusCode();  
  15.   
  16.                 responseHeaders = convertHeaders(httpResponse.getAllHeaders());  
  17.                 // Handle cache validation.  
  18.                 if (statusCode == HttpStatus.SC_NOT_MODIFIED) {  
  19.   
  20.                     Entry entry = request.getCacheEntry();  
  21.                     if (entry == null) {  
  22.                         return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,  null,  
  23.                                 responseHeaders, true,  
  24.                                 SystemClock.elapsedRealtime() – requestStart);  
  25.                     }  
  26.   
  27.                     // A HTTP 304 response does not have all header fields. We  
  28.                     // have to use the header fields from the cache entry plus  
  29.                     // the new ones from the response.  
  30.                     // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5  
  31.                     entry.responseHeaders.putAll(responseHeaders);  
  32.                     return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,  
  33.                             entry.responseHeaders, true,  
  34.                             SystemClock.elapsedRealtime() – requestStart);  
  35.                 }  
  36.   
  37.                 // Some responses such as 204s do not have content.  We must check.  
  38.                 if (httpResponse.getEntity() != null) {  
  39.                   responseContents = entityToBytes(httpResponse.getEntity());  
  40.                 } else {  
  41.                   // Add 0 byte response as a way of honestly representing a  
  42.                   // no-content request.  
  43.                   responseContents = new byte[ 0];  
  44.                 }  
  45.   
  46.                 // if the request is slow, log it.  
  47.                 long requestLifetime = SystemClock.elapsedRealtime() – requestStart;  
  48.                 logSlowRequests(requestLifetime, request, responseContents, statusLine);  
  49.   
  50.                 if (statusCode < 200 || statusCode >  299) {  
  51.                     throw new IOException();  
  52.                 }  
  53.                 return new NetworkResponse(statusCode, responseContents, responseHeaders,  false,  
  54.                         SystemClock.elapsedRealtime() – requestStart);  
  55.             } catch (SocketTimeoutException e) {  
  56.                 attemptRetryOnException(“socket”, request,  new TimeoutError());  
  57.             } catch (ConnectTimeoutException e) {  
  58.                 attemptRetryOnException(“connection”, request,  new TimeoutError());  
  59.             } catch (MalformedURLException e) {  
  60.                 throw new RuntimeException( “Bad URL “ + request.getUrl(), e);  
  61.             } catch (IOException e) {  
  62.                 int statusCode = 0;  
  63.                 NetworkResponse networkResponse = null;  
  64.                 if (httpResponse != null) {  
  65.                     statusCode = httpResponse.getStatusLine().getStatusCode();  
  66.                 } else {  
  67.                     throw new NoConnectionError(e);  
  68.                 }  
  69.                 VolleyLog.e(“Unexpected response code %d for %s”, statusCode, request.getUrl());  
  70.                 if (responseContents != null) {  
  71.                     networkResponse = new NetworkResponse(statusCode, responseContents,  
  72.                             responseHeaders, false, SystemClock.elapsedRealtime() – requestStart);  
  73.                     if (statusCode == HttpStatus.SC_UNAUTHORIZED ||  
  74.                             statusCode == HttpStatus.SC_FORBIDDEN) {  
  75.                         attemptRetryOnException(“auth”,  
  76.                                 request, new AuthFailureError(networkResponse));  
  77.                     } else {  
  78.                         // TODO: Only throw ServerError for 5xx status codes.  
  79.                         throw  new ServerError(networkResponse);  
  80.                     }  
  81.                 } else {  
  82.                     throw new NetworkError(networkResponse);  
  83.                 }  
  84.             }  
  85.         }  
  86.     }  

《【源码分析】Volley 用法及源码分析》

@Override
    public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map<String, String> responseHeaders = Collections.emptyMap();
            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) {

                    Entry entry = request.getCacheEntry();
                    if (entry == null) {
                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
                                responseHeaders, true,
                                SystemClock.elapsedRealtime() - requestStart);
                    }

                    // A HTTP 304 response does not have all header fields. We
                    // have to use the header fields from the cache entry plus
                    // the new ones from the response.
                    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
                    entry.responseHeaders.putAll(responseHeaders);
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                            entry.responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart);
                }

                // 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,
                        SystemClock.elapsedRealtime() - requestStart);
            } catch (SocketTimeoutException e) {
                attemptRetryOnException("socket", request, new TimeoutError());
            } catch (ConnectTimeoutException e) {
                attemptRetryOnException("connection", request, new TimeoutError());
            } catch (MalformedURLException e) {
                throw new RuntimeException("Bad URL " + request.getUrl(), e);
            } catch (IOException e) {
                int statusCode = 0;
                NetworkResponse networkResponse = null;
                if (httpResponse != null) {
                    statusCode = httpResponse.getStatusLine().getStatusCode();
                } else {
                    throw new NoConnectionError(e);
                }
                VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
                if (responseContents != null) {
                    networkResponse = new NetworkResponse(statusCode, responseContents,
                            responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
                    if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                            statusCode == HttpStatus.SC_FORBIDDEN) {
                        attemptRetryOnException("auth",
                                request, new AuthFailureError(networkResponse));
                    } else {
                        // TODO: Only throw ServerError for 5xx status codes.
                        throw new ServerError(networkResponse);
                    }
                } else {
                    throw new NetworkError(networkResponse);
                }
            }
        }
    }

代码挺多,其实关键请求代码就一句话:

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. httpResponse = mHttpStack.performRequest(request, headers);  

《【源码分析】Volley 用法及源码分析》

httpResponse = mHttpStack.performRequest(request, headers);

mHttpStack是上级构造方法传递过来的参数stack。stack就是httpClient和httpUrlConnection的不同选择实现。

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. if (stack == null) {  
  2.             if (Build.VERSION.SDK_INT >= 9) {  
  3.                 stack = new HurlStack();  
  4.             } else {  
  5.                 // Prior to Gingerbread, HttpUrlConnection was unreliable.  
  6.                 // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html  
  7.                 stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));  
  8.             }  
  9.         }  

《【源码分析】Volley 用法及源码分析》

if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

5.结果返回(response返回)

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. mDelivery.postResponse(request, response);  

《【源码分析】Volley 用法及源码分析》

mDelivery.postResponse(request, response);

mDelivery即默认的new ExecutorDelivery(new Handler(Looper.getMainLooper()))

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. public ExecutorDelivery(final Handler handler) {  
  2.         // Make an Executor that just wraps the handler.  
  3.         mResponsePoster = new Executor() {  
  4.             @Override  
  5.             public void execute(Runnable command) {  
  6.                 handler.post(command);  
  7.             }  
  8.         };  
  9.     }  

《【源码分析】Volley 用法及源码分析》

public ExecutorDelivery(final Handler handler) {
        // Make an Executor that just wraps the handler.
        mResponsePoster = new Executor() {
            @Override
            public void execute(Runnable command) {
                handler.post(command);
            }
        };
    }

postResponse
[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. @Override  
  2.     public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {  
  3.         request.markDelivered();  
  4.         request.addMarker(“post-response”);  
  5.         mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));  
  6.     }  

《【源码分析】Volley 用法及源码分析》

@Override
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
        request.markDelivered();
        request.addMarker("post-response");
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
    }

ResponseDeliveryRunnable
[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

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

《【源码分析】Volley 用法及源码分析》

/**
     * A Runnable used for delivering network responses to a listener on the
     * main thread.
     */
    @SuppressWarnings("rawtypes")
    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();
            }
       }
    }

mRequest.deliverResponse(mResponse.result);以StringRequest为例:其中mListener接口为外部传入接口,见文章最开始使用。至于这个parseNetworkResponse则是用来解析网络请求的返回数据response的。

[java]
view plain
copy
print
?
《【源码分析】Volley 用法及源码分析》
《【源码分析】Volley 用法及源码分析》

  1. @Override  
  2.     protected void deliverResponse(String response) {  
  3.         mListener.onResponse(response);  
  4.     }  
  5.   
  6.     @Override  
  7.     protected Response<String> parseNetworkResponse(NetworkResponse response) {  
  8.         String parsed;  
  9.         try {  
  10.             parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));  
  11.         } catch (UnsupportedEncodingException e) {  
  12.             parsed = new String(response.data);  
  13.         }  
  14.         return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));  
  15.     }  

《【源码分析】Volley 用法及源码分析》

@Override
    protected void deliverResponse(String response) {
        mListener.onResponse(response);
    }

    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException e) {
            parsed = new String(response.data);
        }
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }

over~








    原文作者:Android源码分析
    原文地址: https://juejin.im/entry/591d35e5128fe1005cf8dc89
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞