欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

深入了解Volley如何执行一个Request的流程

来源: 开发者 投稿于  被查看 33205 次 评论:189

深入了解Volley如何执行一个Request的流程


Volley主要类构成

Volley: 框架同名类,负责创建RequestQueue对象 Request:代表一个网络请求的抽象类,具体实现子类有(StringRequest,JsonRequest,ImageRequest),需添加到RequestQueue中操作。包含了url,请求方法,请求Header,请求Body,请求的优先等级信息。 RequestQueue:核心类,负责维护所有的Request对象,完成请求操作。该类中有两个基于优先级的Request队列mCacheQueue,mNetWorkQueue(PriorityBlockingQueue),分别是缓存请求队列,网络请求队列。该数据结构支持对添加进来的Request进行优先级排序(所以可以将理财列表内的请求设置成优先级最高),还有一个mCurrentRequest(SetRequest类型,用来维护一个已经加入到Request中,并且还没有完成的请求;还有一个等待请求的集合,mWaitingQueue,如果一个请求正在处理并且可以被缓存,那么后续的相同的url的请求,将被放入到等待队列。 CacheDispatcher:缓存调度线程类,不停的从缓存队列中取出Request请求,进行处理。 NetworkDispatcher:网络调度线程类,不断的从网络请求队列中取出Request去处理。 Cache:缓存接口,代表了一个可以获取请求结果,存储请求结果的缓存。 DiskBasedCache:基于disk的缓存实现类。 BasicNetwork:Volley中默认的网络接口实现类 Response:封装了经过解析后的数据,用于传输 ByteArrayPool:byte[] 的回收池,用于byte[]的再回收利用,减少了内存的分配和回收,是Volley提高性能的优化之一。 RetryPolicy:重试策略接口类 ResponseDelivery:请求结果的传输接口,用于传递请求结果或请求错误。 ExcutorDelivery:请求结果传输接口具体实现类。利用Handler将缓存调度线程或网络调度线程中产生的请求结果和请求错误传输到主线程的回调函数中。

Volley一个请求的执行过程

volley官方文档给出的执行一个请求的步骤是:

RequestQueue mQueue = Volley.newRequestQueue(context);  
mQueue.add(request);

为什么创建一个mQueue以后,直接放入一个request,就可以执行该request了呢?发起网络请求的代码在哪里呢?这里看下newRequestQueue()做了哪些操作:

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 {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

        Network network = new BasicNetwork(stack);

        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        queue.start();

        return queue;
    }

可以看到创建RequestQueue后,调用了start()方法,再看下start()方法:

 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;
            VolleyCompat.getInstance().setNetworkDispatcherPriorityMax(networkDispatcher);
            networkDispatcher.start();
        }
    }

通过源码可以查看到,CacheDispatcher,NetworkDispatcher都是继承了Thread类,这里相当于启动了一个缓存调度线程,四个网络调度线程,这里先不分析缓存,看网络调度线程做了些什么操作:

 @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            Request request;
            try {
                // Take a request from the queue.
                request = mQueue.take();
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }

            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");
                    continue;
                }

                addTrafficStatsTag(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");
                    continue;
                }

                // 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);
            } catch (VolleyError volleyError) {

//                    volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
//                    parseAndDeliverNetworkError(request, volleyError);

                //volley扩展:(2015-11-10)当出现错误,如设备无网络,请求服务器出错,并且有缓存时,需要将缓存内容展示出来
                VolleyCompat.getInstance().showCacheWhenErrorOccur(mCache,request,mDelivery,volleyError,startTimeMs);

            } catch (Exception e) {
                e.printStackTrace();
//                    VolleyLog.e(e, "Unhandled exception %s", e.toString());
//                    VolleyError volleyError = new VolleyError(e);
//                    volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
//                    mDelivery.postError(request, volleyError);

                //volley扩展:(2015-11-10)当出现错误,如设备无网络,请求服务器出错,并且有缓存时,需要将缓存内容展示出来
                VolleyError volleyError = new VolleyError(e);
                VolleyCompat.getInstance().showCacheWhenErrorOccur(mCache,request,mDelivery,volleyError,startTimeMs);
            }
        }
    }

很简单,网络调度线程先是从队列mQueue里取出一个request,然后判断该request是否已经取消,如果没有被用户取消,则调用mNetwork的performRequest(request)发起网络请求,这里的mNetWork是一个BasicNetWrok,看下performRequest()的实现,

@Override
    public NetworkResponse performRequest(Request request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map responseHeaders = Collections.emptyMap();
            try {
                // Gather headers.
                Map headers = new HashMap();
                addCacheHeaders(headers, request.getCacheEntry());
                httpResponse = mHttpStack.performRequest(request, headers);
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();

                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                // Handle cache validation.
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {

                    Entry entry = request.getCacheEntry();
                    if (entry == null) {
                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
                                responseHeaders, true,
                                SystemClock.elapsedRealtime() - requestStart);
                    }

                    // A HTTP 304 response does not have all header fields. We
                    // have to use the header fields from the cache entry plus
                    // the new ones from the response.
                    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
                    entry.responseHeaders.putAll(responseHeaders);
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                            entry.responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart);
                }

                // Some responses such as 204s do not have content.  We must check.
                if (httpResponse.getEntity() != null) {
                  responseContents = entityToBytes(httpResponse.getEntity());
                } else {
                  // Add 0 byte response as a way of honestly representing a
                  // no-content request.
                  responseContents = new byte[0];
                }

                // if the request is slow, log it.
                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,
                        SystemClock.elapsedRealtime() - requestStart);
            } 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, SystemClock.elapsedRealtime() - requestStart);
                    if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                            statusCode == HttpStatus.SC_FORBIDDEN) {
                        attemptRetryOnException("auth",
                                request, new AuthFailureError(networkResponse));
                    } else {
                        // TODO: Only throw ServerError for 5xx status codes.
                        throw new ServerError(networkResponse);
                    }
                } else {
                    throw new NetworkError(networkResponse);
                }
            }
        }
    }

首先通过调用mHttpStack.performRequest(request,headers) 发起请求,返回的首先是一个org.apache.http.HttpResponse对象,根据statusCode判断,如果是SC_NOT_MODIFIED,并且支持缓存,那么直接加载本地缓存,否则根据httpResponse.getEntity()方法来构建一个NetworkResponse的对象。

现在回到NetWorkDispatcher的run()方法里,当得到一个NetWorkResponse对象后,接着调用request.parseNetWorkResponse(networkResponse)来对结果数据进行解析,解析数据的过程是什么样的呢?查看下request的parseNetWorkResponse()方法。

 abstract protected Response parseNetworkResponse(NetworkResponse response);

是给抽象方法,再看Request,也是一个抽象类,说明要想发起请求,必须创建一个Request的子类,并且自己实现该方法,由该Requet来决定如何解析这个networkResponse。好,那先看下Volley自己提供的StringRequest中该方法是怎么实现的:

@Override
    protected Response 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));
    }

对比下另外一个Volley提供的JsonObjectRequest的parseNetWorkResponse实现:

@Override
    protected Response 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.success()会new 出一个Response对象,该对象中的result变量也是泛型,并且该result的类型也是由新建Request类的子类的时候制定的,这样这个请求就得到了一个Response对象。

接着回到NetWorkDispatcher的run()方法,接着分析下边流程:

mDelivery.postResponse(request, response);

这个mDelivery又是什么东西呢?查看NetWorkDispatcher源码,其实是一个ResponseDelivery类型变量,而查看ResponseDelivery发现是一个接口,从这里可以看到,Volley中处处用到了面向接口的编程思想,使得使用者可以自己定义子类型,提供相关服务。那么这个mDelivery是在哪里制定的呢?其实是在RequestQueue的构造方法中指定,然后在start()方法中传递给NetWorkDispatcher的:

public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(cache, network, threadPoolSize,
                new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }
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;
            VolleyCompat.getInstance().setNetworkDispatcherPriorityMax(networkDispatcher);
            networkDispatcher.start();
        }
    }

好,接着看ExecutorDelivery的postResponse()方法:

 @Override
    public void postResponse(Request request, Response response, Runnable runnable) {
        request.markDelivered();
        request.addMarker("post-response");
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
    }

调用了mResponsePoster的execute()方法,接着看下mResponsePoster:

/** 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,想必熟悉handler机制的童鞋们就特别想知道这个handler是哪个线程的handler了吧?会不会是UI Thread的呢?接着看

public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(cache, network, threadPoolSize,
                new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }

可以看到,确实是UI 线程的,这样我们就可以将ResponseDeliveryRunnable放到UI线程里去执行啦。好了,那看下让UI线程执行什么操作呢?

 @SuppressWarnings("unchecked")
        @Override
        public void run() {
            // If this request has canceled, finish it and don't deliver.
            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 this is an intermediate response, add a marker, otherwise we're done
            // and the request can be finished.
            if (mResponse.intermediate) {
                mRequest.addMarker("intermediate-response");
            } else {
                mRequest.finish("done");
            }

            // If we have been provided a post-delivery runnable, run it.
            if (mRunnable != null) {
                mRunnable.run();
            }
       }

其中这个mRequest,就是我们这次网络请求发送的Request,Response就是我们这次请求返回的结果封装类,再次提醒,该Response里的result变量的类型,就是我们在新建Request子类时候指定的类型。好的,万里长城差最后一步了,再看下Request的deliverResponse方法()

/**
     * Subclasses must implement this to perform delivery of the parsed
     * response to their listeners.  The given response is guaranteed to
     * be non-null; responses that fail to parse are not delivered.
     * @param response The parsed response returned by
     * {@link #parseNetworkResponse(NetworkResponse)}
     */
    abstract protected void deliverResponse(T response);

也是一个抽象方法,那么我们还是看下JsonRequest类的该方法是如何实现的:

@Override
    protected void deliverResponse(String response) {
        mListener.onResponse(response);
    }

mListener是Response的一个内部接口:

public class Response {

    /** Callback interface for delivering parsed responses. */
    public interface Listener {
        /** Called when a response is received. */
        public void onResponse(T response);
    }

那mListener是在哪里赋值的呢?

public JsonRequest(int method, String url, String requestBody, Listener listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
        mRequestBody = requestBody;
    }

也就是在创建JsonRequest的时候,就要指定这个Response.Listener,这里也是面相接口编程,因为不知道将来谁会对这个Request请求的结果数据感兴趣,那么就在设计框架的时候,调用listener的onResponse()方法,这样以后谁对结果数据敢兴趣,那就实现接口,并且在创建相应Request的时候,传递进来就可以了。

有一个细节值得注意的就是,在Volley原生Request类的构造函数里,是只有Response.ErrorListener,并没有Response.Listener的:

/**
     * Creates a new request with the given method (one of the values from {@link Method}),
     * URL, and error listener.  Note that the normal response listener is not provided here as
     * delivery of responses is provided by subclasses, who have a better idea of how to deliver
     * an already-parsed response.
     */
    public Request(int method, String url, Response.ErrorListener listener) {
        mMethod = method;
        mUrl = url;
        mErrorListener = listener;
        setRetryPolicy(new DefaultRetryPolicy());

        mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
    }

也就是说请求如果发生错误信息了,那么必须要有能通知到的listener来处理,但是请求成功后,可以任何事情也不做。

正查数据请求过来的流程分析清楚后,最后再看下请求出错后的处理逻辑:

在ExecutorDelivery的run()方法里:

// Deliver a normal response or error, depending.
            if (mResponse.isSuccess()) {
                mRequest.deliverResponse(mResponse.result);
            } else {
                mRequest.deliverError(mResponse.error);
            }

而在Request的deliverError()方法中:

/**
     * Delivers error message to the ErrorListener that the Request was
     * initialized with.
     *
     * @param error Error details
     */
    public void deliverError(VolleyError error) {
        if (mErrorListener != null) {
            mErrorListener.onErrorResponse(error);
        }
    }

也印证了上面的请求出错必须有处理的猜测。

至此,Volley一个请求的执行流程就分析完成了,总结下:

Volley在主线程中创建一个request请求后,就会将该请求放到队列中,然后后台线程(NetWorkDispatcher)从队列中取出请求,调用网络相关类,执行请求,得到返回结果后,通过ExecutorDelivery将一个Runnable对象放到UI线程的消息池里去执行。 在创建Request的时候,需要制定Response.Listener,Response.ErroListener对象,因为在Request执行完后,Volley会通过handler机制,将在UI线程里调用这些listener的相关方法,从而使应用可以在这些listener的相关方法里,获得这些请求结果数据,从而实现刷新UI等操作。

用户评论