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