通过Volley我们能学到什么?(2) — 刨析网络请求框架

来源:互联网 发布:java面向对象练习题 编辑:程序博客网 时间:2024/06/05 06:01
接上一章来讲网络请求的大体过程,Volley是如何封装并使用HttpURLConnection和HttpClient的。

目录
网络请求的基本逻辑
网络请求的交互层—>BasicNetwork
封装HttpURLConnection实现网络连接

RequestQueue.java
从通过Volley我们能学到什么?(1) — 工作原理与设计模式的NetworkDispatcher.java中的调用网络请求开始讲起。。
NetworkResponse networkResponse = mNetwork.performRequest(request);

Volley中网络请求一共分为三层,大体结构如下: 

1、NetworkDispatcher获取Request对象并分发给BasicNetwork;
2、BasicNetwork得到Request对象后,配置头文件等配置信息分发给HttpStack;
3、HttpStack得到Request对象和配置信息后进行真正的网络请求;
4、HttpStack将网络请求的结果返回给BasicNetwork;
5、BasicNetwork得到返回结果,如果成功则返回给NetworkDispatcher,否则处理其他异常情况;

第一层NetworkDispatcher已讲完,这篇主要讲第二层(BasicNetwork)和第三层(HttpStack)。

网络请求的交互层—>BasicNetwork

BasicNetwork.java
这一行代码直接调用的是BasicNetwork.java中的performRequest()方法 
    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());                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);                }            }        }    }

1、首先创建循环机制,使请求失败可以再次请求(3行);
2、添加头部信息(9 - 10行);
3、调用HttpStack对象进行网络请求,进入网络请求的第三层(11行);
4、获取请求结果的状态(12 - 13行);
5、更新头部信息(14行);
6、如果获取的状态行是304则说明之前已获取过数据且服务器未做修改,直接返回上次请求的数据(16 - 32行); 
7、将数据内容写入到responseContents里(35 - 41行);
8、请求完成,如果访问总时长超过3秒将访问的详细信息打印出来(44 - 45行);
9、如果状态码返回是异常,报IO异常,跳转到下面的catch中(47 - 49);
10、如果是连接异常则重新连接(53、55行);
11、如果超时,则跳出循环向上抛异常,超时时间最大值可设置(56行);
12、处理IO异常,如果是因为访问无权限或禁止访问这种认证问题,可以再给一次机会,否则直接抛异常(59 - 80行);

多次网络请求机制
BasicNetwork.java
接下来我们来看一下Volley多次访问逻辑,入口为attemptRetryOnException()方法。 
private static void attemptRetryOnException(String logPrefix, Request<?> request,            VolleyError exception) throws VolleyError {        RetryPolicy retryPolicy = request.getRetryPolicy();        int oldTimeout = request.getTimeoutMs();        try {            retryPolicy.retry(exception);        } catch (VolleyError e) {            request.addMarker(                    String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));            throw e;        }        request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));    }

1、获取Request对象里面的RetryPolicy对象,并获取超时时间(3 - 4行);
2、调用RetryPolicy对象的retry()方法来检测是否需要重新访问(7行);

向下看retry()的源码

DefaultRetryPolicy.java
    public void retry(VolleyError error) throws VolleyError {        mCurrentRetryCount++;        mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);        if (!hasAttemptRemaining()) {            throw error;        }    }

1、重新访问次数加一(2行);
2、累加超时时间(3行);
3、判断重新访问次数是否大于最大访问次数(默认是1,可自定义),大于最大值报异常跳出循环(4行);

封装HttpURLConnection实现网络连接(HttpClient暂不整理)

如果是Android2.3即以上版本调用上面HttpStack对象的performRequest()方法的会映射到HurlStack类里面的performRequest()方法来实现网络连接。

HurlStack.java
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);        // Initialize HttpResponse with data from the HttpURLConnection.        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);        int responseCode = 0;        try {            responseCode = connection.getResponseCode();        } catch (IOException e) {            connection.getErrorStream();            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);        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;

1、得到URL并获取头部信息(3 - 6行);
2、mUrlRewriter是干什么的?mUrlRewriter默认为null,但如果赋值的话,所有使用到的网络请求URL统一替换成mUrlRewriter中的URL,应该是项目测试用的(7 - 13行);
3、创建HttpURLConnection对象,并做一些连接前的配置,看一下这个方法的细节(15行); 
                                   
                                   调用createConnection()方法创建一个HttpURLConnection对象,createConnection()方法里面只是判断了一下是不是通过https访问。如果是https则在HttpsURLConnection这个类里面的两个静态对象初始化,
如果是http的话不做处理。因为如果是http的话后面的执行直接用connection这个引用就可以,如果是https的话由于已经在HttpsURLConnection这个类中把必要的静态数据初始化了所以可以直接将connection强行转换(170行、179 - 181行);
4、设置头部(16 - 18行);
5、设置连接参数,支持8种请求方式(19行);
6、获取连接最终状态,如果是-1说明不能获取到,向上抛异常(22 - 34行);
7、配置BasicHttpResponse对象以便在最后返回它(35 - 37行);
8、将body写入到BasicHttpResponse对象中(38行);
9、将头部信息写入到BasicHttpResponse对象中(39 - 44行);

这样整体的网络结构就差不多了。如果继续深入可以研究上传功能的源码和HttpUrlConnect的源码。




1 0
原创粉丝点击