Volley源碼解析


說點題外話,將近三個半月沒有寫博客了,年初換工作,在新的公司,上班第三天開始干活,花了二十來天弄了一個項目,上線后,接着又迭代了幾個版本,不知不覺,試用期過完,才稍微有幾天閑時。在年初的時候,就一直在想,
將圈內的幾個流行的網絡框架的源碼分析分析,但是又但是水平不夠,有些分析的不好,那就尷尬了....所以花了點時間好好看了一下,走了一遍這些源碼,決定試一試,相當於做個筆記吧。今天就從一個相對輕量級的網絡請求框架下手--Volley。

Volley
提起這個Volley,很多同學應該都很熟悉,但是我面試過蠻多人,問起,對volley的了解,基本上就說,里面對圖片做了緩存,在2.3之前用的是HTTPClient,
2.3后用的是HttpURLConnection,然后就沒了.... 雖然這沒什么錯,但是對於一個有經驗的開發人員來說,這樣的認識,太過於表面了。我們知道Volley是
在2013年Google I/O大會上推出了一個新的網絡通信框架,他的設計目的就是為了那些請求數據量不是特別大,但是又是特別頻繁的網絡操作非常適合。但是對於
數據量較大的請求,比如說下載一個較大的文件,Volley可能相比於其他的框架,就有點不足了....

 

Volley簡單使用
我這里是以依賴架包的形式 ,大家也可以以gradle的形式進行依賴。

 

好了,接下來上代碼了.....

 1         //獲取volley的請求對象
 2         RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
 3         StringRequest stringRequest = new StringRequest(StringRequest.Method.GET, "http://www.baidu.com", new Response.Listener<String>() {
 4             @Override
 5             public void onResponse(String s) {
 6                 Log.d("MainActivity", "----->" + s);
 7 
 8             }
 9         }, new Response.ErrorListener() {
10             @Override
11             public void onErrorResponse(VolleyError volleyError) {
12                 Log.d("MainActivity", "---volleyError-->" + volleyError);
13             }
14         });
15         requestQueue.add(stringRequest);

從代碼可以看出,首先newRequestQueue來獲取到一個請求隊列,然后在將StringRequest這個請求添加到請求隊列中,就可以了,就是這么簡單。當然請求不值
StringRequest,還有JsonObjectRequest ,ImageRequest等等但是用法都是一樣的,這里就不貼代碼了。接着...就沒了,Volley的簡單使用就這樣可以進行請
求了。是不是很簡單....

但是這個不是本篇的重點,重點是分析一下這些是怎么執行的。先上一張圖,這張圖是在網上拿過來用的...

 

 

我們先看看newRequestQueue這個內部是怎么執行的,代碼一開始連續執行了幾個重載方法,最后走到newRequestQueue 

 1 public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
 2         File cacheDir = new File(context.getCacheDir(), "volley");
 3         String userAgent = "volley/0";
 4 
 5         try {
 6             String packageName = context.getPackageName();
 7             PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
 8             userAgent = packageName + "/" + info.versionCode;
 9         } catch (NameNotFoundException var7) {
10             ;
11         }
12 
13         //這里進行了一個版本的判斷 2.3之前用的是HTTPClient,2.3之后使用的是HttpURLConnection
14         if (stack == null) {
15             if (VERSION.SDK_INT >= 9) {
16                 stack = new HurlStack();
17             } else {
18                 stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
19             }
20         }
21 
22         Network network = new BasicNetwork((HttpStack)stack);
23         RequestQueue queue;
24         if (maxDiskCacheBytes <= -1) {
25             queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
26         } else {
27             queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
28         }
29 
30         queue.start();
31         return queue;
32     }

在這里,我們看到了一個版本判斷,是不是瞬間感覺有點熟悉,沒錯,我們前面說的,volley2.3之前用的是HTTPClient,2.3之后使用的是HttpURLConnection
就是在這里進行判斷的。接着看queue.start();

 1 public void start() {
 2         this.stop();
 3         this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
 4         this.mCacheDispatcher.start();
 5 
 6         for(int i = 0; i < this.mDispatchers.length; ++i) {
 7             NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
 8             this.mDispatchers[i] = networkDispatcher;
 9             networkDispatcher.start();
10         }
11 
12     }

mCacheDispatcher是緩存調度線程,NetworkDispatcher是網絡調度線程,而這個this.mDispatchers.length系統默認的大小為4,也就是說,在這里總共啟動了5個線程在后台運行。
好了,到這里,就可以了,看源碼不要每一行都弄懂,不然,出不來了。到這里就拿到了這個RequestQueue對象。回過頭來看前面使用的代碼

 1         //獲取volley的請求對象
 2         RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
 3         StringRequest stringRequest = new StringRequest(StringRequest.Method.GET, "http://www.baidu.com", new Response.Listener<String>() {
 4             @Override
 5             public void onResponse(String s) {
 6                 Log.d("MainActivity", "----->" + s);
 7 
 8             }
 9         }, new Response.ErrorListener() {
10             @Override
11             public void onErrorResponse(VolleyError volleyError) {
12                 Log.d("MainActivity", "---volleyError-->" + volleyError);
13             }
14         });
15         requestQueue.add(stringRequest);

我們拿到這個RequestQueue對象以后,然后就把這個請求通過add方法添加到隊列中,我們看看這個add()方法是怎么執行的。

 1 public <T> Request<T> add(Request<T> request) {
 2         request.setRequestQueue(this);
 3         Set var2 = this.mCurrentRequests;
 4         synchronized(this.mCurrentRequests) {
 5             this.mCurrentRequests.add(request);
 6         }
 7 
 8         request.setSequence(this.getSequenceNumber());
 9         request.addMarker("add-to-queue");
10         if (!request.shouldCache()) { //如果不能緩存
11             this.mNetworkQueue.add(request);
12             return request;
13         } else {
14             Map var7 = this.mWaitingRequests;
15             synchronized(this.mWaitingRequests) {
16                 String cacheKey = request.getCacheKey();
17                 if (this.mWaitingRequests.containsKey(cacheKey)) { //判斷之前是否執行過,但是還沒有返回結果
18                     Queue<Request<?>> stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);
19                     if (stagedRequests == null) {
20                         stagedRequests = new LinkedList();
21                     }
22 
23                     ((Queue)stagedRequests).add(request);
24                     this.mWaitingRequests.put(cacheKey, stagedRequests);
25                     if (VolleyLog.DEBUG) {
26                         VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[]{cacheKey});
27                     }
28                 } else {
29                 //沒有的話就將請求加入緩存隊列mCacheQueue,同時加入mWaitingRequests中用來做下次同樣請求來時的重復判斷依據
30                     this.mWaitingRequests.put(cacheKey, (Object)null);
31                     this.mCacheQueue.add(request);
32                 }
33 
34                 return request;
35             }
36         }
37     }

從代碼中可以看出,首先判斷是否可以緩存,當然,默認是可以緩存的。如果不能緩存的話,則通過this.mNetworkQueue.add(request);將請求添加到網絡請求隊列中。如果可以緩存
則還會判斷一次這個請求是否請求,如果執行過就就通過this.mWaitingRequests.put(cacheKey, stagedRequests);添加到mWaitingRequests隊列,不在重復請求。否則就加入到緩存隊列。
大體的流程是這樣。現在我們看看緩存的,和網絡的是怎么執行的。我們找到start()方法

 1 public void start() {
 2         this.stop();
 3         this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
 4         this.mCacheDispatcher.start();
 5 
 6         for(int i = 0; i < this.mDispatchers.length; ++i) {
 7             NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
 8             this.mDispatchers[i] = networkDispatcher;
 9             networkDispatcher.start();
10         }
11 
12     }

先看CacheDispatcher,找到run()方法

 1 public void run() {
 2         if (DEBUG) {
 3             VolleyLog.v("start new dispatcher", new Object[0]);
 4         }
 5 
 6         Process.setThreadPriority(10);
 7         this.mCache.initialize();
 8 
 9         while(true) {
10             while(true) {
11                 while(true) {
12                     while(true) {
13                         try {
14                             while(true) {
15                                 final Request<?> request = (Request)this.mCacheQueue.take(); //從緩存隊列中獲取到一個請求
16                                 request.addMarker("cache-queue-take");
17                                 if (request.isCanceled()) { //判斷請求是否取消,如果取消了,那就將該請求finish掉
18                                     request.finish("cache-discard-canceled");
19                                 } else {
20                                     Entry entry = this.mCache.get(request.getCacheKey());
21                                     if (entry == null) {//如果從緩存中取出來的內容為空,則將請求加入到網絡線程中再次請求
22                                         request.addMarker("cache-miss");
23                                         this.mNetworkQueue.put(request);
24                                     } else if (entry.isExpired()) { //如果請求過期了,則將請求加入到網絡線程中再次請求
25                                         request.addMarker("cache-hit-expired");
26                                         request.setCacheEntry(entry);
27                                         this.mNetworkQueue.put(request);
28                                     } else { //將數據回調到主線程
29                                         request.addMarker("cache-hit");
30                                         Response<?> response = request.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
31                                         request.addMarker("cache-hit-parsed");
32                                         if (entry.refreshNeeded()) {
33                                             request.addMarker("cache-hit-refresh-needed");
34                                             request.setCacheEntry(entry);
35                                             response.intermediate = true;
36                                             this.mDelivery.postResponse(request, response, new Runnable() {
37                                                 public void run() {
38                                                     try {
39                                                         CacheDispatcher.this.mNetworkQueue.put(request);
40                                                     } catch (InterruptedException var2) {
41                                                         ;
42                                                     }
43 
44                                                 }
45                                             });
46                                         } else {
47                                             this.mDelivery.postResponse(request, response);
48                                         }
49                                     }
50                                 }
51                             }
52                         } catch (InterruptedException var4) {
53                             if (this.mQuit) {
54                                 return;
55                             }
56                         }
57                     }
58                 }
59             }
60         }
61     }

這里嵌套了幾個循環,有點凌亂啊,但是慢慢分析的話,就會發現,其實很清晰。我在注釋上面寫了,這里就不重復了

我們在看看NetworkDispatcher,看看網絡線程是怎么執行的。一樣找到run()方法

 1 public void run() {
 2         Process.setThreadPriority(10);
 3 
 4         while(true) {
 5             long startTimeMs;
 6             Request request;
 7             while(true) {
 8                 startTimeMs = SystemClock.elapsedRealtime();
 9 
10                 try {
11                     request = (Request)this.mQueue.take(); //獲取到一個請求
12                     break;
13                 } catch (InterruptedException var6) {
14                     if (this.mQuit) {
15                         return;
16                     }
17                 }
18             }
19 
20             try {
21                 request.addMarker("network-queue-take");
22                 if (request.isCanceled()) { //如果請求取消了,則將請求finish掉
23                     request.finish("network-discard-cancelled");
24                 } else {//進行網絡請求
25                     this.addTrafficStatsTag(request);
26                     NetworkResponse networkResponse = this.mNetwork.performRequest(request);
27                     request.addMarker("network-http-complete");
28                     if (networkResponse.notModified && request.hasHadResponseDelivered()) {
29                         request.finish("not-modified");
30                     } else {
31                         Response<?> response = request.parseNetworkResponse(networkResponse);
32                         request.addMarker("network-parse-complete");
33                         if (request.shouldCache() && response.cacheEntry != null) {
34                             this.mCache.put(request.getCacheKey(), response.cacheEntry);
35                             request.addMarker("network-cache-written");
36                         }
37 
38                         request.markDelivered();
39                         this.mDelivery.postResponse(request, response);
40                     }
41                 }
42             } catch (VolleyError var7) {
43                 var7.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
44                 this.parseAndDeliverNetworkError(request, var7);
45             } catch (Exception var8) {
46                 VolleyLog.e(var8, "Unhandled exception %s", new Object[]{var8.toString()});
47                 VolleyError volleyError = new VolleyError(var8);
48                 volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
49                 this.mDelivery.postError(request, volleyError);
50             }
51         }
52     }

代碼比較多,我們直接找到NetworkResponse networkResponse = this.mNetwork.performRequest(request);這句代碼,這句代碼就是請求網絡的代碼,最核心的。performRequest是一個接口,我們看看這個
performRequest()方法。Network在最開始說版本判斷的時候里面有一句代碼Network network = new BasicNetwork((HttpStack)stack); 從這句代碼,我們可以知道BasicNetwork才是最終
實現網絡請求的類,我們找到performRequest方法

 1 public NetworkResponse performRequest(Request<?> request) throws VolleyError {
 2         long requestStart = SystemClock.elapsedRealtime();
 3 
 4         while(true) {
 5             HttpResponse httpResponse = null;
 6             byte[] responseContents = null;
 7             Map responseHeaders = Collections.emptyMap();
 8 
 9             try {
10                 Map<String, String> headers = new HashMap();
11                 this.addCacheHeaders(headers, request.getCacheEntry());
12                 httpResponse = this.mHttpStack.performRequest(request, headers);
13                 StatusLine statusLine = httpResponse.getStatusLine();
14                 int statusCode = statusLine.getStatusCode();
15                 responseHeaders = convertHeaders(httpResponse.getAllHeaders());
16                 if (statusCode == 304) {
17                     Entry entry = request.getCacheEntry();
18                     if (entry == null) {
19                         return new NetworkResponse(304, (byte[])null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
20                     }
21 
22                     entry.responseHeaders.putAll(responseHeaders);
23                     return new NetworkResponse(304, entry.data, entry.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
24                 }
25 
26                 if (statusCode == 301 || statusCode == 302) {
27                     String newUrl = (String)responseHeaders.get("Location");
28                     request.setRedirectUrl(newUrl);
29                 }
30 
31                 byte[] responseContents;
32                 if (httpResponse.getEntity() != null) {
33                     responseContents = this.entityToBytes(httpResponse.getEntity());
34                 } else {
35                     responseContents = new byte[0];
36                 }
37 
38                 long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
39                 this.logSlowRequests(requestLifetime, request, responseContents, statusLine);
40                 if (statusCode >= 200 && statusCode <= 299) {
41                     return new NetworkResponse(statusCode, responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
42                 }
43 
44                 throw new IOException();
45             } catch (SocketTimeoutException var12) {
46                 attemptRetryOnException("socket", request, new TimeoutError());
47             } catch (ConnectTimeoutException var13) {
48                 attemptRetryOnException("connection", request, new TimeoutError());
49             } catch (MalformedURLException var14) {
50                 throw new RuntimeException("Bad URL " + request.getUrl(), var14);
51             } catch (IOException var15) {
52                 int statusCode = false;
53                 NetworkResponse networkResponse = null;
54                 if (httpResponse == null) {
55                     throw new NoConnectionError(var15);
56                 }
57 
58                 int statusCode = httpResponse.getStatusLine().getStatusCode();
59                 if (statusCode != 301 && statusCode != 302) {
60                     VolleyLog.e("Unexpected response code %d for %s", new Object[]{statusCode, request.getUrl()});
61                 } else {
62                     VolleyLog.e("Request at %s has been redirected to %s", new Object[]{request.getOriginUrl(), request.getUrl()});
63                 }
64 
65                 if (responseContents == null) {
66                     throw new NetworkError(networkResponse);
67                 }
68 
69                 networkResponse = new NetworkResponse(statusCode, (byte[])responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
70                 if (statusCode != 401 && statusCode != 403) {
71                     if (statusCode != 301 && statusCode != 302) {
72                         throw new ServerError(networkResponse);
73                     }
74 
75                     attemptRetryOnException("redirect", request, new AuthFailureError(networkResponse));
76                 } else {
77                     attemptRetryOnException("auth", request, new AuthFailureError(networkResponse));
78                 }
79             }
80         }
81     }

代碼比較多,但是大多數代碼是判斷狀態返回碼的,不需要理會。我們直接看httpResponse = this.mHttpStack.performRequest(request, headers);這一句代碼,HttpStack這個有沒有很熟悉。沒有??沒關系我在復制一次代碼

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

還是在這個版本判斷這里,這里就是HurlStack就是真正的網絡請求的類了,網絡請求,就是寫在這個類里面的。好了,volley整個流程大概就是這樣了。現在大家回過頭看最初的哪一張圖,是不是明了很多。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM