Volley之网络请求NetworkDispatcher
在源码解析Volley请求队列RequestQueue一文最后,我们捋了向请求队列添加Request网络请求的逻辑:
<T> void beginRequest(Request<T> request) { // If the request is uncacheable, skip the cache queue and go straight to the network. if (!request.shouldCache()) { sendRequestOverNetwork(request); } else { mCacheQueue.add(request); } }
对于没有缓存需求的网络请求,sendRequestOverNetwork(Request<T> request)放入网络请求队列mNetworkQueue中等待处理:
<T> void sendRequestOverNetwork(Request<T> request) { mNetworkQueue.add(request); }
1、NetworkDispatcher
NetworkDispatcher继承自Thread。
/** * Provides a thread for performing network dispatch from a queue of requests. */ public class NetworkDispatcher extends Thread { ... }
1.1、NetworkDispatcher初始化
在源码解析Volley请求队列RequestQueue一文开启请求队列start()方法中构造特定数量的网络调度程序线程,也就是NetworkDispatcher线程实例,并启动。
/** Starts the dispatchers in this queue. */ public void start() { stop(); // Make sure any currently running dispatchers are stopped. // Create the cache dispatcher and start it. mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery); mCacheDispatcher.start(); // Create network dispatchers (and corresponding threads) up to the pool size. for (int i = 0; i < mDispatchers.length; i++) { NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery); mDispatchers[i] = networkDispatcher; networkDispatcher.start(); } }
NetworkDispatcher的构造方法如下,使用RequestQueue队列中的成员变量作为参数初始化NetworkDispatcher。
/ ** *创建一个新的网络调度程序线程,必须先调用start()才能开启处理。 * * @param queue传入分类请求队列 * @param network用于执行请求的网络接口 * @param cache缓存接口,用于将响应写入缓存 * @param delivery交付界面,用于发布Response * / public NetworkDispatcher(BlockingQueue<Request<?>> queue, Network network, Cache cache, ResponseDelivery delivery) { mQueue = queue; mNetwork = network; mCache = cache; mDelivery = delivery; }
1.2、NetworkDispatcher成员
NetworkDispatcher中的成员变量有如下5个,前四个都是持有RequestQueue请求队列中的变量引用。
/** The queue of requests to service. */ private final BlockingQueue<Request<?>> mQueue; /** The network interface for processing requests. */ private final Network mNetwork; /** The cache to write to. */ private final Cache mCache; /** For posting responses and errors. */ private final ResponseDelivery mDelivery; /** Used for telling us to die. */ private volatile boolean mQuit = false;
1.3、启动NetworkDispatcher
在RequestQueue的start()方法中,每创建一个NetworkDispatcher实例,就调用该实例的start()方法开启网络调度程序线程。Thread的start()方法我们无需关注,看NetworkDispatcher重写的的线程run()方法
@Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); while (true) { try { processRequest(); } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { Thread.currentThread().interrupt(); return; } VolleyLog.e( "Ignoring spurious interrupt of NetworkDispatcher thread; " + "use quit() to terminate it"); } } }
可以看到每一个RequestQueue网络调度程序线程启动之后都会无线循环,不断的从RequestQueue中的拿取网络请求并处理。
1.4、关闭NetworkDispatcher
RequestQueue提供了一个stop()方法,停止请求队列。挨个调用每个调度程序的quit()方法
/** Stops the cache and network dispatchers. */ public void stop() { if (mCacheDispatcher != null) { mCacheDispatcher.quit(); } for (final NetworkDispatcher mDispatcher : mDispatchers) { if (mDispatcher != null) { mDispatcher.quit(); } } }
RequestQueue网络调度程序线程的quit()方法实现如下,将标志位mQuit置为true,再调用Thread的interrupt()终止当前线程。会在前面的run()方法中被捕获到终止while(true){}循环。
/** * Forces this dispatcher to quit immediately. If any requests are still in the queue, they are * not guaranteed to be processed. */ public void quit() { mQuit = true; interrupt(); }
2、处理网络请求
开启RequestQueue网络调度程序线程后,开始循环处理网络请求队列中的请求。
2.1、processRequest二级调用
在主循环中不断的调用processRequest()方法,而在该方法中从RequestQueue的网络请求阻塞队列BlockingQueue<Request<?>>中take()网络请求处理。
之所以单独再抽取一个方法,注释写的很清楚明白:提取到单独的方法中,避免在不确定的时间内保持先前的请求引用有效的必要条件。
// Extracted to its own method to ensure locals have a constrained liveness scope by the GC. // This is needed to avoid keeping previous request references alive for an indeterminate amount // of time. Update consumer-proguard-rules.pro when modifying this. See also // https://github.com/google/volley/issues/114 private void processRequest() throws InterruptedException { // Take a request from the queue. Request<?> request = mQueue.take(); processRequest(request); }
下面才是真正处理请求的方法processRequest(Request<?> request),代码量非常精简,其中大部分操作都被提取到Network接口中,而这里从Volley类层层初始化,经过RequestQueue传递过来的NetWork的默认实现是BasicNetwork。
void processRequest(Request<?> request) { long startTimeMs = SystemClock.elapsedRealtime(); request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_STARTED); try { request.addMarker("network-queue-take"); // If the request was cancelled already, do not perform the // network request. if (request.isCanceled()) { request.finish("network-discard-cancelled"); request.notifyListenerResponseNotUsable(); return; } addTrafficStatsTag(request); // Perform the network request. NetworkResponse networkResponse = mNetwork.performRequest(request); request.addMarker("network-http-complete"); // If the server returned 304 AND we delivered a response already, // we're done -- don't deliver a second identical response. if (networkResponse.notModified && request.hasHadResponseDelivered()) { request.finish("not-modified"); request.notifyListenerResponseNotUsable(); return; } // Parse the response here on the worker thread. Response<?> response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete"); // Write to cache if applicable. // TODO: Only update cache metadata instead of entire record for 304s. if (request.shouldCache() && response.cacheEntry != null) { mCache.put(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written"); } // Post the response back. 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); } }
2.2、BasicNetwork封装网络
BasicNetwork是Volley开源库提供的Network接口默认实现,performRequest(Request<?> request)实现如下,执行网络请求并将结果封装成NetworkResponse返回。
@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 { ... //这里是关键,再次通过HttpStack完成网络请求 httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders); ... return new NetworkResponse( statusCode, responseContents, /* notModified= */ false, SystemClock.elapsedRealtime() - requestStart, responseHeaders); } catch (IOException e) { // This will either throw an exception, breaking us from the loop, or will loop // again and retry the request. NetworkUtility.handleException( request, e, requestStart, httpResponse, responseContents); } } }
2.3、BaseHttpStack封装网络
HttpStack是HTTP堆栈抽象接口,已被Deprecate。
@Deprecated public interface HttpStack { /** * Performs an HTTP request with the given parameters. * * <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise, * and the Content-Type header is set to request.getPostBodyContentType(). * * @param request the request to perform * @param additionalHeaders additional headers to be sent together with {@link * Request#getHeaders()} * @return the HTTP response */ HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError; }
取而代之的是BaseHttpStack抽象类:
/** An HTTP stack abstraction. */ @SuppressWarnings("deprecation") // for HttpStack public abstract class BaseHttpStack implements HttpStack { ... }
Volley框架提供了BaseHttpStack抽象类的默认实现:HurlStack。
回想到在Volley类中创建RequestQueue的时候,有下面一段代码进行了SDK版本的判断。这是因为Android API 2.3以下HttpUrlConnection无法使用,而 API 2.3 以上采用HttpUrlConnection进行连接。
if (Build.VERSION.SDK_INT >= 9) { //Android 2.3以后使用内置的HurlStack network = new BasicNetwork(new HurlStack()); } else { // Prior to Gingerbread, HttpUrlConnection was unreliable. // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html // At some point in the future we'll move our minSdkVersion past Froyo and can // delete this fallback (along with all Apache HTTP code). String userAgent = "volley/0"; try { String packageName = context.getPackageName(); PackageInfo info = context.getPackageManager().getPackageInfo(packageName, /* flags= */ 0); userAgent = packageName + "/" + info.versionCode; } catch (NameNotFoundException e) { } network = new BasicNetwork( new HttpClientStack(AndroidHttpClient.newInstance(userAgent))); }
HurlStack实现的executeRequest(Request<?> request, Map<String, String> additionalHeaders)方法,内部是借助HttpURLConnection实现网络的访问。
@Override public HttpResponse executeRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { //从Request中拿到请求的Url链接,这是在构造请求时传入的 String url = request.getUrl(); HashMap<String, String> map = new HashMap<>(); map.putAll(additionalHeaders); // Request.getHeaders() takes precedence over the given additional (cache) headers). 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 URL parsedUrl = new URL(url); //进行一些设置,并且打开网络连接HttpURLConnection HttpURLConnection connection = openConnection(parsedUrl, request); boolean keepConnectionOpen = false; try { for (String headerName : map.keySet()) { connection.setRequestProperty(headerName, map.get(headerName)); } setConnectionParametersForRequest(connection, request); // Initialize HttpResponse with data from the HttpURLConnection. int responseCode = connection.getResponseCode(); if (responseCode == -1) { // -1 is returned by getResponseCode() if the response code could not be retrieved. // Signal to the caller that something was wrong with the connection. throw new IOException("Could not retrieve response code from HttpUrlConnection."); } if (!hasResponseBody(request.getMethod(), responseCode)) { return new HttpResponse(responseCode, convertHeaders(connection.getHeaderFields())); } // Need to keep the connection open until the stream is consumed by the caller. Wrap the // stream such that close() will disconnect the connection. keepConnectionOpen = true; return new HttpResponse( responseCode, convertHeaders(connection.getHeaderFields()), connection.getContentLength(), createInputStream(request, connection)); } finally { if (!keepConnectionOpen) { connection.disconnect(); } } }
下面这个方法开发者一定很熟悉,openConnection(URL url, Request<?> request)打开网络连接并且进行常见的网络连接设置:
/** * Opens an {@link HttpURLConnection} with parameters. * * @param url * @return an open connection * @throws IOException */ private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException { HttpURLConnection connection = createConnection(url); int timeoutMs = request.getTimeoutMs(); connection.setConnectTimeout(timeoutMs); connection.setReadTimeout(timeoutMs); connection.setUseCaches(false); connection.setDoInput(true); // use caller-provided custom SslSocketFactory, if any, for HTTPS if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) { ((HttpsURLConnection) connection).setSSLSocketFactory(mSslSocketFactory); } return connection; }
调用下面的方法createConnection(URL url)用URL打开HttpURLConnection连接:
protected HttpURLConnection createConnection(URL url) throws IOException { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setInstanceFollowRedirects(HttpURLConnection.getFollowRedirects()); return connection; }
3、网络请求结果回调
经过上面一系列的网络请求,最终回到NetworkDispatcher的processRequest(Request<?> request)方法中,将结果传递给业务方。
@VisibleForTesting void processRequest(Request<?> request) { try { ... Response<?> response = request.parseNetworkResponse(networkResponse); ... mDelivery.postResponse(request, response); } catch (VolleyError volleyError) {} catch (Exception e) {} finally { request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_FINISHED); } }
3.1、ResponseDelivery
ResponseDelivery是Volley控件中定义的请求结果分发接口:
public interface ResponseDelivery { /** Parses a response from the network or cache and delivers it. */ void postResponse(Request<?> request, Response<?> response); /** * Parses a response from the network or cache and delivers it. The provided Runnable will be * executed after delivery. */ void postResponse(Request<?> request, Response<?> response, Runnable runnable); /** Posts an error for the given request. */ void postError(Request<?> request, VolleyError error); }
ResponseDelivery接口默认实现类是ExecutorDelivery。有能力的开发者可以实现自己的ResponseDelivery,学会造零件。
/** Used for posting responses, typically to the main thread. */ private final Executor mResponsePoster; /** * Creates a new response delivery interface. * * @param handler {@link Handler} to post responses on */ public ExecutorDelivery(final Handler handler) { // Make an Executor that just wraps the handler. mResponsePoster = new Executor() { @Override public void execute(Runnable command) { handler.post(command); } }; }
结果通过Handler执行,在RequestQueue的构造方法中,通过主线程的Main Looper创建主线程Handler。所有的网络请求结果回调在主线程中。
public RequestQueue(Cache cache, Network network, int threadPoolSize) { this( cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper()))); }
3.2、响应传递任务:ResponseDeliveryRunnable
ResponseDeliveryRunnable将请求和结果包装在一起成为一个响应传递任务。
private static 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; } @Override public void run() { if (mRequest.isCanceled()) { mRequest.finish("canceled-at-delivery"); return; } // Deliver a normal response or error, depending. if (mResponse.isSuccess()) { mRequest.deliverResponse(mResponse.result); } else { mRequest.deliverError(mResponse.error); } ... // If we have been provided a post-delivery runnable, run it. if (mRunnable != null) { mRunnable.run(); } } }
3.3、成功回调:Request.deliverResponse(T response)
在3.2节中的响应传递任务run()方法中,调用Request的分发方法回调Response结果。deliverResponse(String response)是抽象类Request中的抽象方法。以StringRequest为例,deliverResponse(String response)
@Override protected void deliverResponse(String response) { Response.Listener<String> listener; synchronized (mLock) { listener = mListener; } if (listener != null) { listener.onResponse(response); } }
结果回调接口Listener<T>定义在Response<T>类中。也就是我们构造Request网络请求时传入的回调接口实例。
/** Callback interface for delivering parsed responses. */ public interface Listener<T> { /** Called when a response is received. */ void onResponse(T response); }
3.4、失败回调:Request.deliverError(VolleyError error)
Volley为错误回调提供一个统一的方法deliverError(VolleyError error),并且定义一个公共错误类VolleyError。
public void deliverError(VolleyError error) { Response.ErrorListener listener; synchronized (mLock) { listener = mErrorListener; } if (listener != null) { listener.onErrorResponse(error); } }
和 结果回调接口Listener<T>一样,错误回调接口也是定义在Response<T>类中。
/** Callback interface for delivering error responses. */ public interface ErrorListener { /** * Callback method that an error has been occurred with the provided error code and optional * user-readable message. */ void onErrorResponse(VolleyError error); }
关于Volley的网络请求就到这里,别忘了Volley自带缓存。