對於安卓的網絡開發,咱們可能會用到谷歌自家的網絡請求框架Volley,下面來看一下Volley是怎樣進行網絡請求的。所謂知其所然,並知因此然。因而用本身僅有的水平分析一下其源碼,不對的地方歡迎提出。緩存
使用Volley框架進行網絡開發,通常是三個步驟,首先是初始化一個 RequsetQueue:網絡
RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
第二步是定義一個Request,這裏以StringRequest爲例:框架
StringRequest request = new StringRequest(url, new Response.Listener<String>() { @Override public void onResponse(String s) { }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError volleyError) { } });
最後一步是將Request添加到請求隊列RequestQueue中:socket
requestQueue.add(request);
而後在配置文件中加入網絡請求的權限就能夠實現網絡請求了。ide
接下來就看看它究竟是怎麼完成網絡請求的:函數
首先是 Volley.newRequestQueue(getApplicationContext()),查找一下源碼能夠看到最終調用該三參構造函數:oop
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) { File cacheDir = new File(context.getCacheDir(), "volley"); String userAgent = "volley/0"; try { String network = context.getPackageName(); PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0); userAgent = network + "/" + queue.versionCode; } catch (NameNotFoundException var7) { ; } if(stack == null) { if(VERSION.SDK_INT >= 9) { stack = new HurlStack(); } else { stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); } } BasicNetwork network1 = new BasicNetwork((HttpStack)stack); RequestQueue queue1; if(maxDiskCacheBytes <= -1) { queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1); } else { queue1 = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network1); } queue1.start(); return queue1; }
能夠看到它首先是建立了一個文件,而後判斷 HttpStack 是否爲空,由於咱們調用的是一參構造,因此默認 HttpStack 爲NULL,因而new了一個 HurlStack,這裏主要來看一下HurlStack是什麼:post
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { String url = request.getUrl(); HashMap map = new HashMap(); map.putAll(request.getHeaders()); map.putAll(additionalHeaders); if(this.mUrlRewriter != null) { String parsedUrl = this.mUrlRewriter.rewriteUrl(url); if(parsedUrl == null) { throw new IOException("URL blocked by rewriter: " + url); } url = parsedUrl; } URL parsedUrl1 = new URL(url); HttpURLConnection connection = this.openConnection(parsedUrl1, request); Iterator responseCode = map.keySet().iterator(); while(responseCode.hasNext()) { String protocolVersion = (String)responseCode.next(); connection.addRequestProperty(protocolVersion, (String)map.get(protocolVersion)); } setConnectionParametersForRequest(connection, request); ProtocolVersion protocolVersion1 = new ProtocolVersion("HTTP", 1, 1); int responseCode1 = connection.getResponseCode(); if(responseCode1 == -1) { throw new IOException("Could not retrieve response code from HttpUrlConnection."); } else { BasicStatusLine responseStatus = new BasicStatusLine(protocolVersion1, connection.getResponseCode(), connection.getResponseMessage()); BasicHttpResponse response = new BasicHttpResponse(responseStatus); response.setEntity(entityFromConnection(connection)); Iterator var12 = connection.getHeaderFields().entrySet().iterator(); while(var12.hasNext()) { Entry header = (Entry)var12.next(); if(header.getKey() != null) { BasicHeader h = new BasicHeader((String)header.getKey(), (String)((List)header.getValue()).get(0)); response.addHeader(h); } } return response; } } static void setConnectionParametersForRequest(HttpURLConnection connection, Request<?> request) throws IOException, AuthFailureError { switch(request.getMethod()) { case -1: byte[] postBody = request.getPostBody(); if(postBody != null) { connection.setDoOutput(true); connection.setRequestMethod("POST"); connection.addRequestProperty("Content-Type", request.getPostBodyContentType()); DataOutputStream out = new DataOutputStream(connection.getOutputStream()); out.write(postBody); out.close(); } break; case 0: connection.setRequestMethod("GET"); break; case 1: connection.setRequestMethod("POST"); addBodyIfExists(connection, request); break; case 2: connection.setRequestMethod("PUT"); addBodyIfExists(connection, request); break; case 3: connection.setRequestMethod("DELETE"); break; case 4: connection.setRequestMethod("HEAD"); break; case 5: connection.setRequestMethod("OPTIONS"); break; case 6: connection.setRequestMethod("TRACE"); break; case 7: connection.setRequestMethod("PATCH"); addBodyIfExists(connection, request); break; default: throw new IllegalStateException("Unknown method type."); } }
能夠看到其實就是對HttpUrlConnection的封裝,包括了各類請求方法,並將請求結果封裝成一個 Response返回。如今再回到前面。ui
建立完 HurlStack 以後有建立了一個 BasicNetwork,來看一下這個對象的做用:this
public BasicNetwork(HttpStack httpStack) { this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE)); } public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) { this.mHttpStack = httpStack; this.mPool = pool; } public NetworkResponse performRequest(Request<?> request) throws VolleyError { long requestStart = SystemClock.elapsedRealtime(); while(true) { HttpResponse httpResponse = null; Object responseContents = null; Map responseHeaders = Collections.emptyMap(); try { HashMap e = new HashMap(); this.addCacheHeaders(e, request.getCacheEntry()); httpResponse = this.mHttpStack.performRequest(request, e); StatusLine statusCode1 = httpResponse.getStatusLine(); int networkResponse1 = statusCode1.getStatusCode(); responseHeaders = convertHeaders(httpResponse.getAllHeaders()); if(networkResponse1 == 304) { Entry requestLifetime2 = request.getCacheEntry(); if(requestLifetime2 == null) { return new NetworkResponse(304, (byte[])null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart); } requestLifetime2.responseHeaders.putAll(responseHeaders); return new NetworkResponse(304, requestLifetime2.data, requestLifetime2.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart); } if(networkResponse1 == 301 || networkResponse1 == 302) { String requestLifetime = (String)responseHeaders.get("Location"); request.setRedirectUrl(requestLifetime); } byte[] responseContents1; if(httpResponse.getEntity() != null) { responseContents1 = this.entityToBytes(httpResponse.getEntity()); } else { responseContents1 = new byte[0]; } long requestLifetime1 = SystemClock.elapsedRealtime() - requestStart; this.logSlowRequests(requestLifetime1, request, responseContents1, statusCode1); if(networkResponse1 >= 200 && networkResponse1 <= 299) { return new NetworkResponse(networkResponse1, responseContents1, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart); } throw new IOException(); } catch (SocketTimeoutException var12) { attemptRetryOnException("socket", request, new TimeoutError()); } catch (ConnectTimeoutException var13) { attemptRetryOnException("connection", request, new TimeoutError()); } catch (MalformedURLException var14) { throw new RuntimeException("Bad URL " + request.getUrl(), var14); } catch (IOException var15) { boolean statusCode = false; NetworkResponse networkResponse = null; if(httpResponse == null) { throw new NoConnectionError(var15); } int statusCode2 = httpResponse.getStatusLine().getStatusCode(); if(statusCode2 != 301 && statusCode2 != 302) { VolleyLog.e("Unexpected response code %d for %s", new Object[]{Integer.valueOf(statusCode2), request.getUrl()}); } else { VolleyLog.e("Request at %s has been redirected to %s", new Object[]{request.getOriginUrl(), request.getUrl()}); } if(responseContents == null) { throw new NetworkError(networkResponse); } networkResponse = new NetworkResponse(statusCode2, (byte[])responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart); if(statusCode2 != 401 && statusCode2 != 403) { if(statusCode2 != 301 && statusCode2 != 302) { throw new ServerError(networkResponse); } attemptRetryOnException("redirect", request, new AuthFailureError(networkResponse)); } else { attemptRetryOnException("auth", request, new AuthFailureError(networkResponse)); } } } }
能夠看出實際上是對請求結果的進一步分析和加工,其中二參構造中new了一個 ByteArrayPool(DEFAULT_POOL_SIZE),這個對象就是記錄了最後一次緩存的內容和目前爲止的緩存大小。DEFAULT_POOL_SIZE爲一次緩存的上限,默認大小爲4096,並將其保存到容器中。如今再回到開始。
第一次的話,默認 maxDiskCacheBytes 爲-1,因此執行 queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1),來看一下 DiskBasedCache 的做用:
public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) { this.mEntries = new LinkedHashMap(16, 0.75F, true); this.mTotalSize = 0L; this.mRootDirectory = rootDirectory; this.mMaxCacheSizeInBytes = maxCacheSizeInBytes; } public DiskBasedCache(File rootDirectory) { this(rootDirectory, 5242880); }
能夠看到初始化了緩存路徑和緩存的總大小5M。將一些準備工做作好以後,而後new出RequestQueue對象:
public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) { this.mSequenceGenerator = new AtomicInteger(); this.mWaitingRequests = new HashMap(); this.mCurrentRequests = new HashSet(); this.mCacheQueue = new PriorityBlockingQueue(); this.mNetworkQueue = new PriorityBlockingQueue(); this.mFinishedListeners = new ArrayList(); this.mCache = cache; this.mNetwork = network; this.mDispatchers = new NetworkDispatcher[threadPoolSize]; this.mDelivery = delivery; } public RequestQueue(Cache cache, Network network, int threadPoolSize) { this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper()))); } public RequestQueue(Cache cache, Network network) { this(cache, network, 4); }
能夠看到RequestQueue初始化了一些容器,並採用原子方式,並默認線程的數量爲4. 好了,第一步的分析基本就到這了。如今來看第二步:
第二步就是new了一個 Request對象,來看一下發生了什麼:
public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener) { super(method, url, errorListener); this.mListener = listener; } public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) { this(0, url, listener, errorListener); }
能夠看到谷歌提供的StringRequest默認使用的方法是0,也就是GET方法,接着看一下Request中發生了什麼:
public Request(int method, String url, ErrorListener listener) { this.mEventLog = MarkerLog.ENABLED?new MarkerLog():null; this.mShouldCache = true; this.mCanceled = false; this.mResponseDelivered = false; this.mRequestBirthTime = 0L; this.mCacheEntry = null; this.mMethod = method; this.mUrl = url; this.mErrorListener = listener; this.setRetryPolicy(new DefaultRetryPolicy()); this.mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url); }
基本上是一些配置,默認緩存,DefaultRetryPolicy對象設置了請求的基本設置,findDefaultTrafficStatsTag方法返回了 url 的hashCode。
接下來看第三步:rquestQueue.add(request);
public <T> Request<T> add(Request<T> request) { request.setRequestQueue(this); Set var2 = this.mCurrentRequests; synchronized(this.mCurrentRequests) { this.mCurrentRequests.add(request); } request.setSequence(this.getSequenceNumber()); request.addMarker("add-to-queue"); if(!request.shouldCache()) { this.mNetworkQueue.add(request); return request; } else { Map var7 = this.mWaitingRequests; synchronized(this.mWaitingRequests) { String cacheKey = request.getCacheKey(); if(this.mWaitingRequests.containsKey(cacheKey)) { Object stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey); if(stagedRequests == null) { stagedRequests = new LinkedList(); } ((Queue)stagedRequests).add(request); this.mWaitingRequests.put(cacheKey, stagedRequests); if(VolleyLog.DEBUG) { VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[]{cacheKey}); } } else { this.mWaitingRequests.put(cacheKey, (Object)null); this.mCacheQueue.add(request); } return request; } } }
能夠看到將請求request加入到set集合,保證沒有重複請求,而後判斷請求是否須要緩存,默認爲true,判斷等待隊列中是否有該請求的緩存key,即url,如有則取出對應的Queue集合,將請求加入其中,若沒有則爲該請求建立一個新的Queue。並將之加入到緩存隊列中去。
在回到第一步,new出來一個RequestQueue以後,其緊接着調用了對象方法start(),來看一下RequestQueue的start()方法作了什麼:
public void start() { this.stop(); this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery); this.mCacheDispatcher.start(); for(int i = 0; i < this.mDispatchers.length; ++i) { NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery); this.mDispatchers[i] = networkDispatcher; networkDispatcher.start(); } }
首先調用stop(),代碼以下:
public void stop() { if(this.mCacheDispatcher != null) { this.mCacheDispatcher.quit(); } for(int i = 0; i < this.mDispatchers.length; ++i) { if(this.mDispatchers[i] != null) { this.mDispatchers[i].quit(); } } }
能夠看到以上兩個方法都涉及到了CacheDispatcher和NetworkDispatcher,這兩個類則真正的進行數據請求。首先看一下CacheDispatcher:
public void quit() { this.mQuit = true; this.interrupt(); } public void run() { if(DEBUG) { VolleyLog.v("start new dispatcher", new Object[0]); } Process.setThreadPriority(10); this.mCache.initialize(); while(true) { while(true) { while(true) { while(true) { try { while(true) { final Request e = (Request)this.mCacheQueue.take(); e.addMarker("cache-queue-take"); if(e.isCanceled()) { e.finish("cache-discard-canceled"); } else { Entry entry = this.mCache.get(e.getCacheKey()); if(entry == null) { e.addMarker("cache-miss"); this.mNetworkQueue.put(e); } else if(entry.isExpired()) { e.addMarker("cache-hit-expired"); e.setCacheEntry(entry); this.mNetworkQueue.put(e); } else { e.addMarker("cache-hit"); Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders)); e.addMarker("cache-hit-parsed"); if(entry.refreshNeeded()) { e.addMarker("cache-hit-refresh-needed"); e.setCacheEntry(entry); response.intermediate = true; this.mDelivery.postResponse(e, response, new Runnable() { public void run() { try { CacheDispatcher.this.mNetworkQueue.put(e); } catch (InterruptedException var2) { ; } } }); } else { this.mDelivery.postResponse(e, response); } } } } } catch (InterruptedException var4) { if(this.mQuit) { return; } } } } } } }
它繼承了Theard類,因此是個線程,首先執行quit,防止有線程在執行。接着調用start,即執行run()方法,首先從緩存隊列中取出請求,思路很清晰,從緩存的內容中尋找該請求的url,若是找到了,則將內容取出,並返回。若失敗,則加入網絡的請求隊列中。來看一下網絡請求調度類NetworkDispatcher:
public void run() { Process.setThreadPriority(10); while(true) { long startTimeMs; Request request; while(true) { startTimeMs = SystemClock.elapsedRealtime(); try { request = (Request)this.mQueue.take(); break; } catch (InterruptedException var6) { if(this.mQuit) { return; } } } try { request.addMarker("network-queue-take"); if(request.isCanceled()) { request.finish("network-discard-cancelled"); } else { this.addTrafficStatsTag(request); NetworkResponse e = this.mNetwork.performRequest(request); request.addMarker("network-http-complete"); if(e.notModified && request.hasHadResponseDelivered()) { request.finish("not-modified"); } else { Response volleyError1 = request.parseNetworkResponse(e); request.addMarker("network-parse-complete"); if(request.shouldCache() && volleyError1.cacheEntry != null) { this.mCache.put(request.getCacheKey(), volleyError1.cacheEntry); request.addMarker("network-cache-written"); } request.markDelivered(); this.mDelivery.postResponse(request, volleyError1); } } } catch (VolleyError var7) { var7.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); this.parseAndDeliverNetworkError(request, var7); } catch (Exception var8) { VolleyLog.e(var8, "Unhandled exception %s", new Object[]{var8.toString()}); VolleyError volleyError = new VolleyError(var8); volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); this.mDelivery.postError(request, volleyError); } } }
它和緩存調度線程是同樣的,默認是4個線程在工做。它用到了咱們第一步時建立的HurlStack,經過HttpUrlConnection請求網絡數據,並判斷該請求是否緩存(默認爲true),將請求的url做爲key,將內存做爲數據保存在緩存中。此外Volley提供了當緩存內容大於默認的緩存最大值時,自動清除最遠最不常使用的緩存內容。
至此,一個簡單的網絡請求就完成了,若有不正確的地方,歡迎提出,並及時改正。