对于安卓的网络开发,咱们可能会用到谷歌自家的网络请求框架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提供了当缓存内容大于默认的缓存最大值时,自动清除最远最不常使用的缓存内容。
至此,一个简单的网络请求就完成了,若有不正确的地方,欢迎提出,并及时改正。