共计 15201 个字符,预计需要花费 39 分钟才能阅读完成。
本文大篇幅参考此篇文章 ,大家可以结合两篇文章看一下
1. Volley简介
在很早以前,如果Android开发者想使用网络请求的话,必须自己通过HttpClient或者HttpURLConnection编写代码来访问。但是他两的用法还是很复杂的,如果不适当的封装的话,就会有很多多余代码甚至效率降低。所以当时出现了很多第三方网络通信框架,但是都是第三方的,而谷歌官方一直没有作为。 最终在2013年,谷歌终于意识到了问题,于是他们推出了一个官方的全新的网络框架——Volley。Volley它又能非常简单的进行HTTP通信,又能轻松加载网络上的图片。他的设计目的就是应对数据量不大但是频发的网络操作,但是对于下载等需要大数据量的网络操作,他就不太适合。
2. 源码解析
2.1 从RequestQueue入手
如果你使用Volley的话,就会发现Volley不管进行什么操作,首先第一步就是先创建RequestQueue对象。 所以我们就可以认定他为Volley的入口。 创建RequestQueue的方法是RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());,我们就看看newRequestQueue干了什么:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
public static RequestQueue newRequestQueue (Context context, BaseHttpStack stack) { BasicNetwork network; if (stack == null ) { if (Build.VERSION.SDK_INT >= 9 ) { network = new BasicNetwork (new HurlStack ()); } else { String userAgent = "volley/0" ; try { String packageName = context.getPackageName(); PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0 ); userAgent = packageName + "/" + info.versionCode; } catch (NameNotFoundException e) { } network = new BasicNetwork ( new HttpClientStack (AndroidHttpClient.newInstance(userAgent))); } } else { network = new BasicNetwork (stack); } return newRequestQueue(context, network); }
调用方法后,先查看Android版本是否大于等于2.3,如果大于则调用基于HttpURLConnection的HurlStack,否则调用基于HttpClient的HttpClientStack。接下来创建BasicNetwork并调用newRequestQueue(context, network)方,我们再来看看这个newRequestQueue()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
private static RequestQueue newRequestQueue (Context context, Network network) { final Context appContext = context.getApplicationContext(); DiskBasedCache.FileSupplier cacheSupplier = new DiskBasedCache .FileSupplier() { private File cacheDir = null ; @Override public File get () { if (cacheDir == null ) { cacheDir = new File (appContext.getCacheDir(), DEFAULT_CACHE_DIR); } return cacheDir; } }; RequestQueue queue = new RequestQueue (new DiskBasedCache (cacheSupplier), network); queue.start(); return queue; }
可以看到,这个方法主要为Volley创建了一个硬盘缓存DiskBasedCache,然后通过这个磁盘缓存和Network创建了一个RequestQueue对象,并调用了start()方法,接下来我们看下start()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
public void start () { stop(); mCacheDispatcher = new CacheDispatcher (mCacheQueue, mNetworkQueue, mCache, mDelivery); mCacheDispatcher.start(); for (int i = 0 ; i < mDispatchers.length; i++) { NetworkDispatcher networkDispatcher = new NetworkDispatcher (mNetworkQueue, mNetwork, mCache, mDelivery); mDispatchers[i] = networkDispatcher; networkDispatcher.start(); } }
CacheDispatcher是一个缓存调度线程,并调用了start()方法。在循环中调用NetworkDispatcher的start()方法。NetworkDispatcher是网络调度线程,默认情况下mDispatchers.length为4,默认开启了4个调度线程,外加1个缓存调度线程,总共5个线程。 接下来Volley会创建各种Request,并调用RequestQueue的add()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
public <T> Request<T> add (Request<T> request) { request.setRequestQueue(this ); synchronized (mCurrentRequests) { mCurrentRequests.add(request); } request.setSequence(getSequenceNumber()); request.addMarker("add-to-queue" ); sendRequestEvent(request, RequestEvent.REQUEST_QUEUED); if (!request.shouldCache()) { mNetworkQueue.add(request); return request; } mCacheQueue.add(request); return request; }
这块地方的代码就很简单了,就是根据request的shouldCache()方法来返回request的mShouldCache属性来判断是否可以缓存,默认是可以的。如果能缓存,将此请求加入mCacheQueue队列,不再重复请求;不可以的话就将请求加入网络请求队列mNetworkQueue。
2.2 CacheDispatcher缓存调度线程
RequestQueue的add()方法并没有请求网络或者对缓存进行操作。当将请求添加到网络请求队 列或者缓存队列时,在后台的网络调度线程和缓存调度线程轮询各自的请求队列,若发现有请求任务则开 始执行。下面先看看缓存调度线程。
首先先来看看CacheDispatcher的add()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
@Override public void run () { if (DEBUG) VolleyLog.v("start new dispatcher" ); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); mCache.initialize(); while (true ) { try { processRequest(); } catch (InterruptedException e) { if (mQuit) { Thread.currentThread().interrupt(); return ; } VolleyLog.e( "Ignoring spurious interrupt of CacheDispatcher thread; " + "use quit() to terminate it" ); } } }
这块可以看出主要就是初始化了缓存队列,然后开了个死循环,一直调用processRequest(),我们来看看这个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
private void processRequest () throws InterruptedException { final Request<?> request = mCacheQueue.take(); processRequest(request); } @VisibleForTesting void processRequest (final Request<?> request) throws InterruptedException { request.addMarker("cache-queue-take" ); request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_STARTED); try { if (request.isCanceled()) { request.finish("cache-discard-canceled" ); return ; } Cache.Entry entry = mCache.get(request.getCacheKey()); if (entry == null ) { request.addMarker("cache-miss" ); if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) { mNetworkQueue.put(request); } return ; } if (entry.isExpired()) { request.addMarker("cache-hit-expired" ); request.setCacheEntry(entry); if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) { mNetworkQueue.put(request); } return ; } request.addMarker("cache-hit" ); Response<?> response = request.parseNetworkResponse( new NetworkResponse (entry.data, entry.responseHeaders)); request.addMarker("cache-hit-parsed" ); if (!entry.refreshNeeded()) { mDelivery.postResponse(request, response); } else { request.addMarker("cache-hit-refresh-needed" ); request.setCacheEntry(entry); response.intermediate = true ; if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) { mDelivery.postResponse( request, response, new Runnable () { @Override public void run () { try { mNetworkQueue.put(request); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }); } else { mDelivery.postResponse(request, response); } } } finally { request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_FINISHED); } }
我们在processRequest中可以看到有一个方法经常出现,那就是mWaitingRequestManager.maybeAddToWaitingRequests(request),它的作用是判断当前这个request是否有存在相同缓存键的请求已经处于运行状态,如果有,那么就将这个request加入到一个等待队列中,等到相同缓存键的请求完成。
总结一下CacheDispatcher主要步骤:
从CacheQueue中循环取出request;
如果缓存丢失,加入到NetworkQueue中;
如果缓存过期,加入到NetworkQueue中;
将缓存中的数据解析成Response对象;
如果不需要更新,直接将结果回调到主线程,回调操作等介绍完NetworkDispatcher之后一起深入剖析;
如果需要更新,先将结果回调到主线程,然后再将request加入到NetworkQueue中。
下面来看看网络调度线程。
2.3 NetWorkDispatcher网络调度线程
NetworkDispatcher的run方法代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
@Override public void run () { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); while (true ) { try { processRequest(); } catch (InterruptedException e) { if (mQuit) { Thread.currentThread().interrupt(); return ; } VolleyLog.e( "Ignoring spurious interrupt of NetworkDispatcher thread; " + "use quit() to terminate it" ); } } }
由此可以看出,NetWordDispatch和CacheDispatch非常类似。 他的run()方法和CacheDispatch的方法基本一样,这就不多做介绍,下面来看看他的processRequest()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
private void processRequest () throws InterruptedException { Request<?> request = mQueue.take(); processRequest(request); } @VisibleForTesting void processRequest (Request<?> request) { long startTimeMs = SystemClock.elapsedRealtime(); request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_STARTED); try { request.addMarker("network-queue-take" ); if (request.isCanceled()) { request.finish("network-discard-cancelled" ); request.notifyListenerResponseNotUsable(); return ; } addTrafficStatsTag(request); NetworkResponse networkResponse = mNetwork.performRequest(request); request.addMarker("network-http-complete" ); if (networkResponse.notModified && request.hasHadResponseDelivered()) { request.finish("not-modified" ); request.notifyListenerResponseNotUsable(); return ; } Response<?> response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete" ); if (request.shouldCache() && response.cacheEntry != null ) { mCache.put(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written" ); } request.markDelivered(); mDelivery.postResponse(request, response); request.notifyListenerResponseReceived(response); } catch (VolleyError volleyError) { volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); parseAndDeliverNetworkError(request, volleyError); request.notifyListenerResponseNotUsable(); } 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); request.notifyListenerResponseNotUsable(); } finally { request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_FINISHED); } }
通过NetworkDispatcher.processRequest()方法可以发现,主要分为以下几步:
通过BasicNetwork.performRequest(request)得到NetworkResponse对象;
通过request.parseNetworkResponse(networkResponse)解析得到Response对象;
通过mDelivery将成功结果或者失败结果回调到主线程。
现在我们依次来分析这三步:
请求网络,得到NetworkResponse 我们看看BasicNetwork的performRequest()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
@Override public NetworkResponse performRequest (Request<?> request) throws VolleyError { long requestStart = SystemClock.elapsedRealtime(); while (true ) { HttpResponse httpResponse = null ; byte [] responseContents = null ; List<Header> responseHeaders = Collections.emptyList(); try { Map<String, String> additionalRequestHeaders = getCacheHeaders(request.getCacheEntry()); httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders); int statusCode = httpResponse.getStatusCode(); responseHeaders = httpResponse.getHeaders(); if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) { Entry entry = request.getCacheEntry(); if (entry == null ) { return new NetworkResponse ( HttpURLConnection.HTTP_NOT_MODIFIED, null , true , SystemClock.elapsedRealtime() - requestStart, responseHeaders); } …………省略
通过上面源码可以看出,BasicNetwork就是封装了一下NetworkResponse,并没有涉及到网络请求,我们继续深入到BaseHttpStack.executeRequest(request, additionalRequestHeaders)源码中。
1 2 3
public abstract HttpResponse executeRequest ( Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError;
这时发现这个BaseHttpStack就是一个抽象类,而这个executeRequest()也就是一个抽象方法。我当时就卡在这了,调用了一个抽象类的抽象方法,这咋操作嘛。然后我就好好再看了一遍,找到BasicNetwork的构造函数中对mBaseHttpStck定义的地方,发现这个是构造函数传进来的,然后就想到了在调用Volley.newRequestQueue()时,是根据Android版本传入了不同的Stack,那我们就来看看HurlStack.executeRequest()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
@Override public HttpResponse executeRequest (Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { String url = request.getUrl(); HashMap<String, String> map = new HashMap <>(); map.putAll(additionalHeaders); map.putAll(request.getHeaders()); if (mUrlRewriter != null ) { String rewritten = mUrlRewriter.rewriteUrl(url); if (rewritten == null ) { throw new IOException ("URL blocked by rewriter: " + url); } url = rewritten; } URL parsedUrl = new URL (url); HttpURLConnection connection = openConnection(parsedUrl, request); boolean keepConnectionOpen = false ; try { for (String headerName : map.keySet()) { connection.setRequestProperty(headerName, map.get(headerName)); } setConnectionParametersForRequest(connection, request); int responseCode = connection.getResponseCode(); if (responseCode == -1 ) { throw new IOException ("Could not retrieve response code from HttpUrlConnection." ); } if (!hasResponseBody(request.getMethod(), responseCode)) { return new HttpResponse (responseCode, convertHeaders(connection.getHeaderFields())); } keepConnectionOpen = true ; return new HttpResponse ( responseCode, convertHeaders(connection.getHeaderFields()), connection.getContentLength(), new UrlConnectionInputStream (connection)); } finally { if (!keepConnectionOpen) { connection.disconnect(); } } }
可以看到,主要就是借助了HttpURLConnection对象来请求网络,并根据不同的条件返回不同的HttpResponse对象。
解析NetworkResponse, 得到Response: 解析过程是定义在Request类中,但是他是一个抽象类,不同的Request都有自己的实现,我们现在就以JsonRequest为例看看: 然后发现他又是一个抽象类,那我们就看看JsonRequest其中一个实现类JsonObjectRequest的parseNetworkResponse()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
@Override protected Response<JSONObject> parseNetworkResponse (NetworkResponse response) { try { String jsonString = new String ( response.data, HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET)); return Response.success( new JSONObject (jsonString), HttpHeaderParser.parseCacheHeaders(response)); } catch (UnsupportedEncodingException e) { return Response.error(new ParseError (e)); } catch (JSONException je) { return Response.error(new ParseError (je)); } }
这个就不用多说了,根据返回来的response建了一个String然后把这个String放到Response里面去然后再返回去。
回调主线程 回调主要是通过Delivery的postResponse()方法实现的,我们来看看这个方法,找过去又找到了一个ResponseDelivery抽象类,然后又得找他的实现类,这时大家应该记得RequestQueue()的时候初始化了一个Delivery:
1 2 3 4 5 6 7
public RequestQueue (Cache cache, Network network, int threadPoolSize) { this ( cache, network, threadPoolSize, new ExecutorDelivery (new Handler (Looper.getMainLooper()))); }
他返回了一个ExecutorDelivery,我们来看看这个类,然后就惊喜的发现,我们终于找到我们需要的东西了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
public ExecutorDelivery (final Handler handler) { mResponsePoster = new Executor () { @Override public void execute (Runnable command) { handler.post(command); } }; }
知道了它的初始化,我们再来看看它是如何实现回调的:
Volley中回调是通过postResponse()方法的 :
1 2 3 4 5 6 7 8 9 10
public void postResponse (Request<?> request, Response<?> response) { postResponse(request, response, null ); } @Override public void postResponse (Request<?> request, Response<?> response, Runnable runnable) { request.markDelivered(); request.addMarker("post-response" ); mResponsePoster.execute(new ResponseDeliveryRunnable (request, response, runnable)); }
postResponse()最终会调用mResponsePoster对象的execute()方法,传入了一个ResponseDeliveryRunnable对象,它实现了Runnable接口,execute()方法会通过Handler.post(runnable)将ResponseDeliveryRunnable放入消息队列。最后我们来看看这个ResponseDeliveryRunnable的run()方法在主线程中做了什么操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
@SuppressWarnings("unchecked") @Override public void run () { if (mRequest.isCanceled()) { mRequest.finish("canceled-at-delivery" ); return ; } if (mResponse.isSuccess()) { mRequest.deliverResponse(mResponse.result); } else { mRequest.deliverError(mResponse.error); } if (mResponse.intermediate) { mRequest.addMarker("intermediate-response" ); } else { mRequest.finish("done" ); } if (mRunnable != null ) { mRunnable.run(); } }
3 请求流程图
最后附上Volley的请求流程图