在2013年 Google I/O 大会上推出了一个网络通信框架 —— Volley ,基于 Android 系统中主要提供了两种方式来进行 HTTP 通信,HttpURLConnection 和 HttpClient (Android M 之后已经删除了 HttpClient )。
使用 Volley的用法非常简单。
StringRequest 1 RequestQueue queue = Volley.newRequestQueue(context);
这里拿到的 RequestQueue
是一个请求队列对象,它可以缓存所有的HTTP请求,然后按照一定的算法并发地发出这些请求。
1 2 3 4 5 6 7 8 9 10 StringRequest stringRequest = new StringRequest("http://yydcdut.com" , new Response.Listener<String>() { @Override public void onResponse (String response) { } }, new Response.ErrorListener() { @Override public void onErrorResponse (VolleyError error) { } });
StringRequest
的构造函数需要传入三个参数,第一个参数就是目标服务器的 URL 地址 ,第二个参数是服务器响应成功的回调 ,第三个参数是服务器响应失败的回调 。
1 queue.add(stringRequest);
将这个 StringRequest
对象添加到 RequestQueue
。
同时注意,要访问网络需要访问网络的权限:
1 <uses-permission android:name ="android.permission.INTERNET" />
如果还设置了缓存到 SD 卡上的话,还要加 SD 卡的读写权限。
流程
创建一个RequestQueue对象。
创建一个StringRequest对象。
将StringRequest对象添加到RequestQueue里面。
Post请求 1 2 3 4 5 6 7 8 9 StringRequest stringRequest = new StringRequest(Method.POST, url, listener, errorListener) { @Override protected Map<String, String> getParams() throws AuthFailureError { Map<String, String> map = new HashMap<String, String>(); map.put("params1" , "value1" ); map.put("params2" , "value2" ); return map; } };
1 2 3 4 5 6 7 8 9 StringRequest stringRequest = new StringRequest(Method.POST, url, listener, errorListener) { @Override public Map<String, String> getHeaders() throws AuthFailureError { Map<String, String> map = new HashMap<String, String>(); map.put("header1" , "value1" ); map.put("header2" , "value2" ); return map; } };
JsonRequest 类似于 StringRequest
,JsonRequest
也是继承自 Request
,但是一个抽象类,有两个直接的子类,JsonObjectRequest
和 JsonArrayRequest
。
1 2 3 4 5 6 7 8 9 10 JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("url" , null , new Response.Listener<JSONObject>() { @Override public void onResponse (JSONObject response) { } }, new Response.ErrorListener() { @Override public void onErrorResponse (VolleyError error) { } });
ImageRequest 1 2 3 4 5 6 7 8 9 10 11 12 ImageRequest imageRequest = new ImageRequest("http://yydcdut.com/img/avatar.png" , new Response.Listener<Bitmap>() { @Override public void onResponse (Bitmap response) { imageView.setImageBitmap(response); } }, 0 , 0 , Config.RGB_565, new Response.ErrorListener() { @Override public void onErrorResponse (VolleyError error) { imageView.setImageResource(R.drawable.default_image); } });
ImageRequest
的构造函数接收六个参数,第一个参数就是图片的URL地址 。第二个参数是图片请求成功的回调 。第三第四个参数分别用于指定允许图片最大的宽度和高度 ,如果指定的网络图片的宽度或高度大于这里的最大值,则会对图片进行压缩,指定成0的话就表示不管图片有多大,都不会进行压缩。第五个参数用于指定图片的颜色属性,Bitmap.Config
下的几个常量都可以在这里使用。第六个参数是图片请求失败的回调 。
源码 源码的讲解还是按照上文使用
的流程来分析。
RequestQueue 从 Volley.newRequestQueue(context);
开始:
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 private static final String DEFAULT_CACHE_DIR = "volley" ;public static RequestQueue newRequestQueue (Context context, HttpStack stack) { File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR); String userAgent = "volley/0" ; try { String packageName = context.getPackageName(); PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0 ); userAgent = packageName + "/" + info.versionCode; } catch (NameNotFoundException e) { } if (stack == null ) { if (Build.VERSION.SDK_INT >= 9 ) { stack = new HurlStack(); } else { stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); } } Network network = new BasicNetwork(stack); RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); queue.start(); return queue; } public static RequestQueue newRequestQueue (Context context) { return newRequestQueue(context, null ); }
在判断 HttpStack
是否为空的时候进行了 Android 版本的判断,如果大于等于9,则使用 HurlStack
,否则使用 HttpClientStack
,两者均继承于接口 HttpStack
,但 HurlStack
的实现是基于 HttpUrlConnection
的,HttpClientStack
的实现是基于 HttpClient
的,之所以在这里进行了版本判断,是因为在小于9的 Android 版本中,HttpUrlConnection
存在着一些 bug,比如对一个可读的 InputStream
调用 close()
方法时,就有可能会导致连接池失效了。但是在 Android 2.3 版本之后,HttpUrlConnection
进行了很大的修正和优化,比如默认 GZip 、Https 方面的改进、增加缓存机制等。
创建好了 HttpStack
之后,接下来又创建了一个 Network
对象,它是用于根据传入的 HttpStack
对象来处理网络请求的,紧接着 new 出一个 RequestQueue
对象,并调用它的 start()
方法进行启动,然后将 RequestQueue
返回。
那么分别来看看 BasicNetwork
、 RequestQueue
、和 queue.start();
做了那些事:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class BasicNetwork implements Network { private static int DEFAULT_POOL_SIZE = 4096 ; protected final HttpStack mHttpStack; protected final ByteArrayPool mPool; public BasicNetwork (HttpStack httpStack) { this (httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE)); } public BasicNetwork (HttpStack httpStack, ByteArrayPool pool) { mHttpStack = httpStack; mPool = pool; } }
在 BasicNetwork
中有一个 byte[] 复用池。目的在于不要频繁创建生命周期比较短的 byte[] 对象会使堆频繁分配位置以及在 Android 垃圾回收导致的延时。
Simply creating and disposing such buffers in the conventional manner can considerable heap churn and garbage collection delays on Android, which lacks good management of short-lived heap objects. It may be advantageous to trade off some memory in the form of a permanently allocated pool of buffers in order to gain heap performance improvements.
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 public class RequestQueue { private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4 ; private final Cache mCache; private final Network mNetwork; private final ResponseDelivery mDelivery; private NetworkDispatcher[] mDispatchers; public RequestQueue (Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) { mCache = cache; mNetwork = network; mDispatchers = new NetworkDispatcher[threadPoolSize]; 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, DEFAULT_NETWORK_THREAD_POOL_SIZE); } }
cache 传入的是 DiskBasedCache
,继承 Cache
类,基于 Disk 的缓存实现类。 ExecutorDelivery
为请求结果传输类,其父类为 ResponseDelivery
,在 Handler 对应线程中传输缓存调度线程或者网络调度线程中产生的请求结果或请求错误,会在请求成功的情况下调用 Request.deliverResponse()
函数,失败时调用 Request.deliverError()
函数。NetworkDispatcher
继承于 Thread
,用于调度处理请求。启动后会不断从网络请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给 ResponseDelivery
,去执行后续处理,并判断结果是否要进行缓存。
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 public class RequestQueue { private final PriorityBlockingQueue<Request> mNetworkQueue = new PriorityBlockingQueue<Request>(); 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(); } } public void stop () { if (mCacheDispatcher != null ) { mCacheDispatcher.quit(); } for (int i = 0 ; i < mDispatchers.length; i++) { if (mDispatchers[i] != null ) { mDispatchers[i].quit(); } } } }
CacheDispatcher
和 NetworkDispatcher
均继承于 Thread
,那么一旦 Volley.newRequestQueue(context);
开始,就有五个线程再后台运行着,而 CacheDispatcher
是缓存线程,NetworkDispatcher
是网络访问线程。
查看 Volley.newRequestQueue(context) 图
NetworkDispatcher 那么我们先来看看 NetworkDispatcher
:
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 83 84 85 86 87 88 89 90 91 92 93 94 public class NetworkDispatcher extends Thread { private final BlockingQueue<Request> mQueue; private final Network mNetwork; private final Cache mCache; private final ResponseDelivery mDelivery; private volatile boolean mQuit = false ; public NetworkDispatcher (BlockingQueue<Request> queue, Network network, Cache cache, ResponseDelivery delivery) { mQueue = queue; mNetwork = network; mCache = cache; mDelivery = delivery; } public void quit () { mQuit = true ; interrupt(); } @Override public void run () { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); Request request; while (true ) { try { request = mQueue.take(); } catch (InterruptedException e) { if (mQuit) { return ; } continue ; } try { request.addMarker("network-queue-take" ); if (request.isCanceled()) { request.finish("network-discard-cancelled" ); continue ; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { TrafficStats.setThreadStatsTag(request.getTrafficStatsTag()); } NetworkResponse networkResponse = mNetwork.performRequest(request); request.addMarker("network-http-complete" ); if (networkResponse.notModified && request.hasHadResponseDelivered()) { request.finish("not-modified" ); continue ; } 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); } catch (VolleyError volleyError) { parseAndDeliverNetworkError(request, volleyError); } catch (Exception e) { VolleyLog.e(e, "Unhandled exception %s" , e.toString()); mDelivery.postError(request, new VolleyError(e)); } } } private void parseAndDeliverNetworkError (Request<?> request, VolleyError error) { error = request.parseNetworkError(error); mDelivery.postError(request, error); } }
实际上走网络访问请求还是在 Network.performRequest
中进行的,网络访问请求完成之后会返回 NetworkResponse
,再通过 Request.parseNetworkResponse
解析成 Response
,再将 Request
和 Response
通过 Delivery.postResponse
传递出去。
所以这里的流程是:
从 mQueue
中获取 Request
,没有数据一直堵塞在那
拿到 Request
之后通过 Network.performRequest
进行网络访问,返回 NetworkResponse
如果状态码是304则不继续下面操作
通过 Request.parseNetworkResponse
将 NetworkResponse
解析成 Response
Delivery.postResponse
将 Request
和 Response
传递出去
查看 NetworkDispatcher 流程图
那么再来看一下访问网络的 BasicNetwork
:
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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 public class BasicNetwork implements Network { @Override public NetworkResponse performRequest (Request<?> request) throws VolleyError { long requestStart = SystemClock.elapsedRealtime(); while (true ) { HttpResponse httpResponse = null ; byte [] responseContents = null ; Map<String, String> responseHeaders = new HashMap<String, String>(); try { Map<String, String> headers = new HashMap<String, String>(); addCacheHeaders(headers, request.getCacheEntry()); httpResponse = mHttpStack.performRequest(request, headers); StatusLine statusLine = httpResponse.getStatusLine(); int statusCode = statusLine.getStatusCode(); responseHeaders = convertHeaders(httpResponse.getAllHeaders()); if (statusCode == HttpStatus.SC_NOT_MODIFIED) { return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, request.getCacheEntry().data, responseHeaders, true ); } if (httpResponse.getEntity() != null ) { responseContents = entityToBytes(httpResponse.getEntity()); } else { responseContents = new byte [0 ]; } long requestLifetime = SystemClock.elapsedRealtime() - requestStart; logSlowRequests(requestLifetime, request, responseContents, statusLine); if (statusCode < 200 || statusCode > 299 ) { throw new IOException(); } return new NetworkResponse(statusCode, responseContents, responseHeaders, false ); } catch (SocketTimeoutException e) { attemptRetryOnException("socket" , request, new TimeoutError()); } catch (ConnectTimeoutException e) { attemptRetryOnException("connection" , request, new TimeoutError()); } catch (MalformedURLException e) { throw new RuntimeException("Bad URL " + request.getUrl(), e); } catch (IOException e) { int statusCode = 0 ; NetworkResponse networkResponse = null ; if (httpResponse != null ) { statusCode = httpResponse.getStatusLine().getStatusCode(); } else { throw new NoConnectionError(e); } VolleyLog.e("Unexpected response code %d for %s" , statusCode, request.getUrl()); if (responseContents != null ) { networkResponse = new NetworkResponse(statusCode, responseContents, responseHeaders, false ); if (statusCode == HttpStatus.SC_UNAUTHORIZED || statusCode == HttpStatus.SC_FORBIDDEN) { attemptRetryOnException("auth" , request, new AuthFailureError(networkResponse)); } else { throw new ServerError(networkResponse); } } else { throw new NetworkError(networkResponse); } } } } private void addCacheHeaders (Map<String, String> headers, Cache.Entry entry) { if (entry == null ) { return ; } if (entry.etag != null ) { headers.put("If-None-Match" , entry.etag); } if (entry.serverDate > 0 ) { Date refTime = new Date(entry.serverDate); headers.put("If-Modified-Since" , DateUtils.formatDate(refTime)); } } private static Map<String, String> convertHeaders(Header[] headers) { Map<String, String> result = new HashMap<String, String>(); for (int i = 0 ; i < headers.length; i++) { result.put(headers[i].getName(), headers[i].getValue()); } return result; } private byte [] entityToBytes(HttpEntity entity) throws IOException, ServerError { PoolingByteArrayOutputStream bytes = new PoolingByteArrayOutputStream(mPool, (int ) entity.getContentLength()); byte [] buffer = null ; try { InputStream in = entity.getContent(); if (in == null ) { throw new ServerError(); } buffer = mPool.getBuf(1024 ); int count; while ((count = in.read(buffer)) != -1 ) { bytes.write(buffer, 0 , count); } return bytes.toByteArray(); } finally { try { entity.consumeContent(); } catch (IOException e) { VolleyLog.v("Error occured when calling consumingContent" ); } mPool.returnBuf(buffer); bytes.close(); } } }
在 BasicNetwork
中真正走网络访问的实际上是 mHttpStack.performRequest()
,而 mHttpStack
这里是接口,面对抽象编程,在 Volley 中实现是 HurlStack
和 HttpClientStack
,那么来看一下这两个类中对方法 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 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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 public class HurlStack implements HttpStack { @Override public HttpResponse performRequest (Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { String url = request.getUrl(); HashMap<String, String> map = new HashMap<String, String>(); map.putAll(request.getHeaders()); map.putAll(additionalHeaders); 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); for (String headerName : map.keySet()) { connection.addRequestProperty(headerName, map.get(headerName)); } setConnectionParametersForRequest(connection, request); ProtocolVersion protocolVersion = new ProtocolVersion("HTTP" , 1 , 1 ); int responseCode = connection.getResponseCode(); if (responseCode == -1 ) { throw new IOException("Could not retrieve response code from HttpUrlConnection." ); } StatusLine responseStatus = new BasicStatusLine(protocolVersion, connection.getResponseCode(), connection.getResponseMessage()); BasicHttpResponse response = new BasicHttpResponse(responseStatus); response.setEntity(entityFromConnection(connection)); for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) { if (header.getKey() != null ) { Header h = new BasicHeader(header.getKey(), header.getValue().get(0 )); response.addHeader(h); } } return response; } static void setConnectionParametersForRequest (HttpURLConnection connection, Request<?> request) throws IOException, AuthFailureError { switch (request.getMethod()) { case Method.DEPRECATED_GET_OR_POST: byte [] postBody = request.getPostBody(); if (postBody != null ) { connection.setDoOutput(true ); connection.setRequestMethod("POST" ); connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getPostBodyContentType()); DataOutputStream out = new DataOutputStream(connection.getOutputStream()); out.write(postBody); out.close(); } break ; case Method.GET: connection.setRequestMethod("GET" ); break ; case Method.DELETE: connection.setRequestMethod("DELETE" ); break ; case Method.POST: connection.setRequestMethod("POST" ); addBodyIfExists(connection, request); break ; case Method.PUT: connection.setRequestMethod("PUT" ); addBodyIfExists(connection, request); break ; default : throw new IllegalStateException("Unknown method type." ); } } private static void addBodyIfExists (HttpURLConnection connection, Request<?> request) throws IOException, AuthFailureError { byte [] body = request.getBody(); if (body != null ) { connection.setDoOutput(true ); connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType()); DataOutputStream out = new DataOutputStream(connection.getOutputStream()); out.write(body); out.close(); } } private static HttpEntity entityFromConnection (HttpURLConnection connection) { BasicHttpEntity entity = new BasicHttpEntity(); InputStream inputStream; try { inputStream = connection.getInputStream(); } catch (IOException ioe) { inputStream = connection.getErrorStream(); } entity.setContent(inputStream); entity.setContentLength(connection.getContentLength()); entity.setContentEncoding(connection.getContentEncoding()); entity.setContentType(connection.getContentType()); return entity; } }
整个网络访问请求也就是正常的 HttpUrlConnection
的操作。
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 public class HttpClientStack implements HttpStack { @Override public HttpResponse performRequest (Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders); addHeaders(httpRequest, additionalHeaders); addHeaders(httpRequest, request.getHeaders()); onPrepareRequest(httpRequest); HttpParams httpParams = httpRequest.getParams(); int timeoutMs = request.getTimeoutMs(); HttpConnectionParams.setConnectionTimeout(httpParams, 5000 ); HttpConnectionParams.setSoTimeout(httpParams, timeoutMs); return mClient.execute(httpRequest); } static HttpUriRequest createHttpRequest (Request<?> request, Map<String, String> additionalHeaders) throws AuthFailureError { switch (request.getMethod()) { case Method.DEPRECATED_GET_OR_POST: { byte [] postBody = request.getPostBody(); if (postBody != null ) { HttpPost postRequest = new HttpPost(request.getUrl()); postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType()); HttpEntity entity; entity = new ByteArrayEntity(postBody); postRequest.setEntity(entity); return postRequest; } else { return new HttpGet(request.getUrl()); } } case Method.GET: return new HttpGet(request.getUrl()); case Method.DELETE: return new HttpDelete(request.getUrl()); case Method.POST: { HttpPost postRequest = new HttpPost(request.getUrl()); postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); setEntityIfNonEmptyBody(postRequest, request); return postRequest; } case Method.PUT: { HttpPut putRequest = new HttpPut(request.getUrl()); putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); setEntityIfNonEmptyBody(putRequest, request); return putRequest; } default : throw new IllegalStateException("Unknown request method." ); } } }
整个网络访问请求也就是正常的 HttpClient
的操作。
Request.parseNetworkResponse 这里我们用 StringRequest
举栗:
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 public class StringRequest extends Request <String > { @Override protected Response<String> parseNetworkResponse (NetworkResponse response) { String parsed; try { parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); } catch (UnsupportedEncodingException e) { parsed = new String(response.data); } return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response)); } } public class Response <T > { public static <T> Response<T> success (T result, Cache.Entry cacheEntry) { return new Response<T>(result, cacheEntry); } public final T result; public final Cache.Entry cacheEntry; private Response (T result, Cache.Entry cacheEntry) { this .result = result; this .cacheEntry = cacheEntry; this .error = null ; } }
再举个 JsonObjectRequest
的栗子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class JsonObjectRequest extends JsonRequest <JSONObject > { @Override protected Response<JSONObject> parseNetworkResponse (NetworkResponse response) { try { String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); 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)); } } }
ResponseDelivery.postResponse 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 public class ExecutorDelivery implements ResponseDelivery { public ExecutorDelivery (final Handler handler) { mResponsePoster = new Executor() { @Override public void execute (Runnable command) { handler.post(command); } }; } @Override 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)); } private class ResponseDeliveryRunnable implements Runnable { private final Request mRequest; private final Response mResponse; private final Runnable mRunnable; public ResponseDeliveryRunnable (Request request, Response response, Runnable runnable) { mRequest = request; mResponse = response; mRunnable = runnable; } @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(); } } } }
在 ResponseDeliveryRunnable
的 mRequest.deliverResponse(mResponse.result)
中将结果传递出去,来看一下StringRequest
的 deliverResponse()
:
1 2 3 4 5 6 7 8 9 10 11 12 public class StringRequest extends Request <String > { public StringRequest (int method, String url, Listener<String> listener, ErrorListener errorListener) { super (method, url, errorListener); mListener = listener; } @Override protected void deliverResponse (String response) { mListener.onResponse(response); } }
这里,也就是返回给我们数据:
1 2 3 4 5 6 7 8 9 10 StringRequest stringRequest = new StringRequest("http://yydcdut.com" , new Response.Listener<String>() { @Override public void onResponse (String response) { } }, new Response.ErrorListener() { @Override public void onErrorResponse (VolleyError error) { } });
转一圈就这么转回来了。
查看 ResponseDelivery 流程图
说完 NetworkDispatcher
就该说一下 CacheDispatcher
:
CacheDispatcher 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 public class CacheDispatcher extends Thread { @Override public void run () { if (DEBUG) VolleyLog.v("start new dispatcher" ); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); mCache.initialize(); while (true ) { try { final Request request = mCacheQueue.take(); request.addMarker("cache-queue-take" ); if (request.isCanceled()) { request.finish("cache-discard-canceled" ); continue ; } Cache.Entry entry = mCache.get(request.getCacheKey()); if (entry == null ) { request.addMarker("cache-miss" ); mNetworkQueue.put(request); continue ; } if (entry.isExpired()) { request.addMarker("cache-hit-expired" ); request.setCacheEntry(entry); mNetworkQueue.put(request); continue ; } 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 ; mDelivery.postResponse(request, response, new Runnable() { @Override public void run () { try { mNetworkQueue.put(request); } catch (InterruptedException e) { } } }); } } catch (InterruptedException e) { if (mQuit) { return ; } continue ; } } } }
启动后会不断从缓存请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery
去执行后续处理。当结果未缓存过、缓存失效或缓存需要刷新的情况下,该请求都需要重新进入NetworkDispatcher
去调度处理。
查看 CacheDispatcher 流程图
StringRequest 介绍完了 Volley.newRequestQueue(context);
这部分,那么讲解一些准备 Request
,这部分拿 StringRequest
开导举栗子:
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 public class StringRequest extends Request <String > { private final Listener<String> mListener; public StringRequest (int method, String url, Listener<String> listener, ErrorListener errorListener) { super (method, url, errorListener); mListener = listener; } public StringRequest (String url, Listener<String> listener, ErrorListener errorListener) { this (Method.GET, url, listener, errorListener); } @Override protected void deliverResponse (String response) { mListener.onResponse(response); } @Override protected Response<String> parseNetworkResponse (NetworkResponse response) { String parsed; try { parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); } catch (UnsupportedEncodingException e) { parsed = new String(response.data); } return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response)); } }
其实 StringRequest
一看就明白了。
RequestQueue 准备好 Request
之后,就将 Request
通过 queue.add(stringRequest);
添加到 RequesrQueue
中。
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 public class RequestQueue { private AtomicInteger mSequenceGenerator = new AtomicInteger(); private final Set<Request> mCurrentRequests = new HashSet<Request>(); private final Map<String, Queue<Request>> mWaitingRequests = new HashMap<String, Queue<Request>>(); public Request add (Request request) { request.setRequestQueue(this ); synchronized (mCurrentRequests) { mCurrentRequests.add(request); } request.setSequence(getSequenceNumber()); request.addMarker("add-to-queue" ); if (!request.shouldCache()) { mNetworkQueue.add(request); return request; } synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); if (mWaitingRequests.containsKey(cacheKey)) { Queue<Request> stagedRequests = mWaitingRequests.get(cacheKey); if (stagedRequests == null ) { stagedRequests = new LinkedList<Request>(); } stagedRequests.add(request); mWaitingRequests.put(cacheKey, stagedRequests); if (VolleyLog.DEBUG) { VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold." , cacheKey); } } else { mWaitingRequests.put(cacheKey, null ); mCacheQueue.add(request); } return request; } } }
首先会会判断当前的 Request
是否可以缓存,如果不能缓存则在直接加入网络请求队列,可以缓存的话则在加入缓存队列。在默认情况下,每个 Request
都是可以缓存的,当然我们也可以调用 Request.setShouldCache(false)
设置成不可缓存。这里会发现,如果 mWaitingRequests
中有 cacheKey
这个 Key 的话,似乎没有加到 NetworkQueue 或者 CacheQueue 中,是怎么回事呢?
答案:如果一个 Request
完成之后,会调用 Request.finish()
,而正好 Request
的成员变量中有 RequestQueue
:
1 2 3 4 5 6 7 8 public abstract class Request <T > implements Comparable <Request <T >> { void finish (final String tag) { if (mRequestQueue != null ) { mRequestQueue.finish(this ); } } }
再看看 RequestQueue.finish()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class RequestQueue { void finish (Request request) { synchronized (mCurrentRequests) { mCurrentRequests.remove(request); } if (request.shouldCache()) { synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); Queue<Request> waitingRequests = mWaitingRequests.remove(cacheKey); if (waitingRequests != null ) { if (VolleyLog.DEBUG) { VolleyLog.v("Releasing %d waiting requests for cacheKey=%s." , waitingRequests.size(), cacheKey); } mCacheQueue.addAll(waitingRequests); } } } } }
这里就解决了上面那个问题,是因为目前已经有相同 cacheKey 的 Request
在 dispatcher 了,所以加到了等待列表中。
查看 RequestQueue.add(request) 流程图
至此,Volley
的解析就结束了。
总结 总体框架流程图
Volley.newRequestQueue(context)
NetworkDispatcher流程图
CacheDispatcher流程图
ResponseDelivery流程图
RequestQueue.add(request)
参考