Volley之网络请求NetworkDispatcher

Quibbler 2021-2-5 597

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、网络请求结果回调

        经过上面一系列的网络请求,最终回到NetworkDispatcherprocessRequest(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自带缓存。


不忘初心的阿甘
最新回复 (0)
    • 安卓笔记本
      2
        登录 注册 QQ
返回
仅供学习交流,切勿用于商业用途。如有错误欢迎指出:fluent0418@gmail.com