首页 > 代码库 > Volley源码解读(上)

Volley源码解读(上)

Volley框架的使用

Volley网络框架的使用方式绝大部分人都已经很熟悉了。

最简单的就是通过Volley提供的静态方法newRequestQueue(Context context)来返回一个消息队列MessageQueue,然后在需要使用时将构造的网络请求消息添加到队列中去,这样就可以完成网络请求

//定义全局的请求队列
requestQueue=Volley.newRequestQueue(getApplicationContext());
//实例化一个请求,并添加到请求队列中去:
String url = "";
//实例化一个新的StringRequest,参数依次为:请求方式,请求的URL,请求成功的回调接口,请求失败的回调接口。
StringRequest request = new StringRequest(Method.GET, url, new Listener<String>() {
            @Override
            public void onResponse(String arg0) {
                Toast.makeText(MainActivity.this, arg0, Toast.LENGTH_SHORT).show();;
                Log.e("successful", arg0);
            }
        }, new Response.ErrorListener() {

            @Override
            public void onErrorResponse(VolleyError arg0) {
                // TODO Auto-generated method stub
                Toast.makeText(MainActivity.this, arg0.toString(), Toast.LENGTH_SHORT).show();
                Log.e("failed", arg0.toString());
            }
        });
        //为每一个请求设置Tag标记,便于后期对request的管理
        request.setTag("testGet");
        //添加到请求队列中去
        MyApplication.getHttpQueues().add(request)

Volley的具体使用方式可以看这篇博文《Volley框架的使用》

Volley的源码(基于Vollley1.0.11)

在上边可以看到我们通过调用Volley提供的静态方法newRequestQueue来获取到一个消息队列,其源码其实是调用了另一个newRequestQueue方法,源码中有多个不同的newRequestQueue函数

    //所有的newRequestQueue函数都是调用的该函数,传入参数依次为上下文对象,http请求栈,缓存的最大值
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
        //用于缓存的文件路径
        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) {
        }

        //传入的stack为空,也就是没有自定义stack
        if (stack == null) {
            //根据不同的SDK版本使用不同的Stack进行存储
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

        //在构造好存储队列之后,构造网络请求
        Network network = new BasicNetwork(stack);

        //构造请求队列
        RequestQueue queue;
        if (maxDiskCacheBytes <= -1)
        {
            // No maximum size specified
            queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        }
        else
        {
            // Disk cache size specified
            queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
        }
        //让队列开始工作
        queue.start();

        return queue;
    }

//---下边的函数都是调用的第一个函数---
    //传入两个参数
   public static RequestQueue newRequestQueue(Context context, int maxDiskCacheBytes) {
        return newRequestQueue(context, null, maxDiskCacheBytes);
    }   

    //传入两个参数
  public static RequestQueue newRequestQueue(Context context, HttpStack stack)
    {
        return newRequestQueue(context, stack, -1);
    }

    //只有一个传入参数
    public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, null);
    }
    //

由此可以梳理一下调用newRequest()方法之后所发生的的操作。

  • 首先生成了文件的缓存路径

  • 然后判断是否自定义了stack队列,若没有定义队列,则根据SDK版本生成默认的stack,SDK大于9使用HurlStack,否则使用HttpClientStack

  • 声明网络请求的实例

  • 根据传入的maxDiskCacheBytes来构造请求队列

可以看到其工作流程很清晰,其中主要分为stack的创建,network的创建,和requestqueue的创建。

stack创建

//传入的stack为空,也就是没有自定义stack,创建默认的stack
        if (stack == null) {
            //根据不同的SDK版本使用不同的Stack进行存储
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

默认的stack根据SDK版本的不同分为HurlStackHttpClientStack,两者其实都是HttpStack的实现类,主要区别在于适用的SDK版本不一样,HurlStack更加适用于Android版本在2.3和更高版本,因为其内部实现是用了 HttpURLConnection这个类。而HttpClientStack适用于Android版本在2.2和更低版本(现在市面上已经没有了)。

首先来看一下低版本中使用的HttpClientStack的实现:

HttpClientStack

   @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
         //首先是创建http的request
        HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
        addHeaders(httpRequest, additionalHeaders);
        addHeaders(httpRequest, request.getHeaders());
        onPrepareRequest(httpRequest);

    //获取到request请求中的参数
        HttpParams httpParams = httpRequest.getParams();
        int timeoutMs = request.getTimeoutMs();
        // TODO: Reevaluate this connection timeout based on more wide-scale
        // data collection and possibly different for wifi vs. 3G.
        //请求超时机制
        HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
        HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);

    //将Request提交给HttpClient去执行,并将执行结果返回
        return mClient.execute(httpRequest);
    }

在这里边主要有三个动作:

  • 创建HttpRequest

  • 封装参数

  • 将HttpRequest提交给HttpClient去执行(在外部是传入了一个AndroidHttpClient的实例)

其实HurlStack所做的操作和HttpClientStack类似,只是在最后执行网络请求的时候HurlStack使用的是HttpURLConnection,而HttpClientStack使用HttpClient来执行任务。接下来看一下HurlStack的具体实现:

HurlStack:

以下是HurlStack中的核心方法performRequest,实现了HttpStack接口定义的方法

    @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
         //根据请求来获取url地址
        String url = request.getUrl();
    //保存请求头
        HashMap<String, String> map = new HashMap<String, String>();
        map.putAll(request.getHeaders());
        map.putAll(additionalHeaders);
    //如果有需要可以通过UrlRewriter接口重写url地址
        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 connection = openConnection(parsedUrl, request);
    //吧request中请求头的信息添加进去
        for (String headerName : map.keySet()) {
            connection.addRequestProperty(headerName, map.get(headerName));
        }
    //设置请求的参数
        setConnectionParametersForRequest(connection, request);
        // Initialize HttpResponse with data from the HttpURLConnection.
        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
        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.");
        }
        StatusLine responseStatus = new BasicStatusLine(protocolVersion,
                connection.getResponseCode(), connection.getResponseMessage());

    //封装请求的结果
        BasicHttpResponse response = new BasicHttpResponse(responseStatus);
    //从connecttion中取出实体并保存在response中
        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);
            }
        }
    //返回响应response
        return response;
    }

以上这两种方式就是Volley中两种不同的请求队列的方式,在构造完stack之后就需要用构造好的stack来创建一个Netwoork实例。

构造网络请求

 Network network = new BasicNetwork(stack);

在下边这个方法中完成了对网络请求的发起和对返回的response的校验封装,最终返回一个NetworkResponse 实例

@Override
    public NetworkResponse performRequest(Request<?> request) throws VolleyError {

    //记录请求开始的时间
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
       //用于接收响应报文
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map<String, String> responseHeaders = Collections.emptyMap();
            try {
                // Gather headers.
                Map<String, String> headers = new HashMap<String, String>();

        //添加缓存头,从请求中获取需要缓存的字段
                addCacheHeaders(headers, request.getCacheEntry());

        //调用HttpStack定义的方法来执行请求,具体使用哪种stack由当前SDK版本决定
                httpResponse = mHttpStack.performRequest(request, headers);

        //从之前返回的httpResponse中获取返回信息
                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);
                }

                // Handle moved resources重定向url进行请求
                if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
                    String newUrl = responseHeaders.get("Location");
                    request.setRedirectUrl(newUrl);
                }

                // Some responses such as 204s do not have content.  We must check.

        //检查返回的response是否有内容,有的话取出保存,没有的话用byte数组填充。
        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;

        //用llog输出这是一次缓慢的网络请求
                logSlowRequests(requestLifetime, request, responseContents, statusLine);

        //如果返回的状态码小于200或者是大于299,报出异常
                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);
                }
                if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || 
                        statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
                    VolleyLog.e("Request at %s has been redirected to %s", request.getOriginUrl(), request.getUrl());
                } else {
                    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 if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || 
                                statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
                        attemptRetryOnException("redirect",
                                request, new AuthFailureError(networkResponse));
                    } else {
                        // TODO: Only throw ServerError for 5xx status codes.
                        throw new ServerError(networkResponse);
                    }
                } else {
                    throw new NetworkError(networkResponse);
                }
            }
        }
    }

这样就完成了一个Network的工作,实际上的网络请求都是由这一部分来完成。

构造requestQueue

 RequestQueue queue;
        if (maxDiskCacheBytes <= -1)
        {
            // No maximum size specified
            queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        }
        else
        {
            // Disk cache size specified
            queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
        }

        queue.start();

在创建好实际的网络请求类后创建请求队列,并将网络请求类的实例传入,这样在queue开始循环之后就可以不断的进行网络请求的操作。上边的代码显示核心的方法为RequestQueue的构造方法和他的start方法。

 /**
     * @param cache A Cache to use for persisting responses to disk
     * @param network A Network interface for performing HTTP requests
     * @param threadPoolSize Number of network dispatcher threads to create
     * @param delivery A ResponseDelivery interface for posting responses and errors
     * 可以看到主要是该构造函数的使用,其中有用于缓存的cache,用于执行任务的dispatcher,用于定义了网络请求的network,用于返回response和error的delivery
     * 
     */
    public RequestQueue(Cache cache, Network network, int threadPoolSize,
            ResponseDelivery delivery) {
        mCache = cache;
        mNetwork = network;
        mDispatchers = new NetworkDispatcher[threadPoolSize];
        mDelivery = delivery;
    }

    /**
     * Creates the worker pool. Processing will not begin until {@link #start()} is called.
     *
     * @param cache A Cache to use for persisting responses to disk
     * @param network A Network interface for performing HTTP requests
     * @param threadPoolSize Number of network dispatcher threads to create
     */
    public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(cache, network, threadPoolSize,
                new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }

    /**
     * Creates the worker pool. Processing will not begin until {@link #start()} is called.
     *
     * @param cache A Cache to use for persisting responses to disk
     * @param network A Network interface for performing HTTP requests
     */
    public RequestQueue(Cache cache, Network network) {
        this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
    }

在构造好RequestQueue之后就可以通过start方法来开始网络请求,其实主要是开启了NetWorkDispatcher的start方法

    /**
     * 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其实是一个继承自Thread类的工作线程,他通过在run方法中进行不断循环来不停的执行任务,通过start方法来开启线程。下边是其核心的run方法

 @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");

        //检查Request是否已经被取消
                // If the request was cancelled already, do not perform the
                // network request.
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }

        //添加tag标记
                addTrafficStatsTag(request);

        //真正的通过NetWork实例来执行网络请求
                // 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");
                    continue;
                }

        //将response进行拼接组装
                // Parse the response here on the worker thread.
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

        //进行cache的更新
                // 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);
            } 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);
            }
        }
    }

到此为止Volley已经完成了任务队列的初始化和创建,也完成了从任务被提交到任务队列中,到被工作线程取出执行,到结果的返回和拼装,以及最后response的分发的整个流程,我们可以来梳理一下整个过程。

技术分享

这就是整个的请求流程的源码,接下来重点就在于对各类型的Request和Response的解读上。

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    Volley源码解读(上)