关于volley的一些分析-发送与接收

request的默认编码方式是UTF-8,它一共有八种交互方式:

    public interface Method {
        int DEPRECATED_GET_OR_POST = -1;
        int GET = 0;
        int POST = 1;
        int PUT = 2;
        int DELETE = 3;
        int HEAD = 4;
        int OPTIONS = 5;
        int TRACE = 6;
        int PATCH = 7;
    }

request的全局变量:

    //网络交互的方式
    private final int mMethod;

    /** URL of this request. */
    private final String mUrl;

    //报错时的回调方法
    private final Response.ErrorListener mErrorListener;

    //request的编号,用于执行先进先出的顺序
    private Integer mSequence;

    //request的队列
    private RequestQueue mRequestQueue;

    //request所对应的response是否应该被缓存
    private boolean mShouldCache = true;

    //该request是否被取消了
    private boolean mCanceled = false;

    //request所对应的response是否到达
    private boolean mResponseDelivered = false;

    //阙值,当超过此值时须在log中打印出来
    private static final long SLOW_REQUEST_THRESHOLD_MS = 3000;

    //重试策略
    private RetryPolicy mRetryPolicy;

    //当request可以从缓存中恢复但是又必须在网络中刷新时,缓存将在此保存,当返回的状态码为304时,这代表不必将它从缓存中驱逐
    private Cache.Entry mCacheEntry = null;

    //一个标记,用于批量取消request
    private Object mTag;

接下来看一下他的一些方法:

    //返回request交互的方式
    public int getMethod() {
        return mMethod;
    }

    //可以调用cancelAll取消所有带同一tag的request
    public Request<?> setTag(Object tag) {
        mTag = tag;
        return this;
    }

    //为request设置重试策略
    public Request<?> setRetryPolicy(RetryPolicy retryPolicy) {
        mRetryPolicy = retryPolicy;
        return this;
    }

    //通知消息队列该request已经完成。
    void finish(final String tag) {
        if (mRequestQueue != null) {
            mRequestQueue.finish(this);
        }
        .
        .
        .
        .
    }

    //被RequestQueue调用,为request设置编号
    public final Request<?> setSequence(int sequence) {
        mSequence = sequence;
        return this;
    }

    /**
     * Returns the URL of this request.
     */
    public String getUrl() {
        return mUrl;
    }

    //返回request对应的缓存键,默认情况下为url。
    public String getCacheKey() {
        return getUrl();
    }

    //取消该任务
    public void cancel() {
        mCanceled = true;
    }

    //返回额外的HTTP请求头。
    public Map<String, String> getHeaders() throws AuthFailureError {
        return Collections.emptyMap();
    }

    //返回用于POST或PUT的键值对参数。或者可以直接重写getBody(),传输自定义的数据。
    protected Map<String, String> getParams() throws AuthFailureError {
        return null;
    }

    //编码方式。用于转换从getParams得到的参数。并将其传入POST或PUT的内容content中
    protected String getParamsEncoding() {
        return DEFAULT_PARAMS_ENCODING;
    }

    //返回POST或PUT的content类型
    public String getBodyContentType() {
        return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
    }

    //返回POST或PUT即将发送的内容。默认情况下,内容由通过application/x-www-form-urlencoded编码的params组成。当重写该方法时,务必考虑是否要重写getBodyContentType
    public byte[] getBody() throws AuthFailureError {
        Map<String, String> params = getParams();
        if (params != null && params.size() > 0) {
            return encodeParameters(params, getParamsEncoding());
        }
        return null;
    }

    //将params按照application/x-www-form-urlencoded的编码方式转换
    private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
        StringBuilder encodedParams = new StringBuilder();
        try {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
                encodedParams.append('=');
                encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
                encodedParams.append('&');
            }
            return encodedParams.toString().getBytes(paramsEncoding);
        } catch (UnsupportedEncodingException uee) {
            throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
        }
    }

    /**
     * Returns the socket timeout in milliseconds per retry attempt. (This value can be changed
     * per retry attempt if a backoff is specified via backoffTimeout()). If there are no retry
     * attempts remaining, this will cause delivery of a {@link TimeoutError} error.
     */
    public final int getTimeoutMs() {
        return mRetryPolicy.getCurrentTimeout();
    }

    //将此request标记为response已返回
    public void markDelivered() {
        mResponseDelivered = true;
    }

    //子类必须复写该方法解析网络交互返回来的数据并return正确的数据类型。此方法将会在工作线程中调用。如果return null,response将不会被发送。
    abstract protected Response<T> parseNetworkResponse(NetworkResponse response);

    //子类必须复写该方法解析网络错误。
    protected VolleyError parseNetworkError(VolleyError volleyError) {
        return volleyError;
    }

    //子类必须复写该方法执行发送已经被解析好的数据。
    abstract protected void deliverResponse(T response);

接下来讲解一下”重试策略“RetryPolicy。该接口只有三个方法,该接口有一个实现类:DefaultRetryPolicy。默认重试次数为1次,超时时间为2.5秒

    //返回当前超时总时间
    public int getCurrentTimeout();

    //返回当前重试次数
    public int getCurrentRetryCount();

    //申请超时补偿,准备下一次重试
    public void retry(VolleyError error) throws VolleyError;

在NetworkDispatcher中可以看到,request的发送是通过调用BasicNetwork.performRequest(request),而该方法其实是在内部调用了HrulStack.performRequest(request)。那么接下来我们看一下HrulStack的调用过程。

@Override
    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的连接超时时间,读取超时时间等等
        HttpURLConnection connection = openConnection(parsedUrl, request);
        //将给定属性添加到请求头
        for (String headerName : map.keySet()) {
            connection.addRequestProperty(headerName, map.get(headerName));
        }
        //根据网络交互方式的不同将connection设置为GET或POST等等,其中如果交互方式为POST,PUT,那connection将调用request.getBody填充需发送的内容和request.getBosyContentType定义发送内容类型
        setConnectionParametersForRequest(connection, request);
        // Initialize HttpResponse with data from the HttpURLConnection.
        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
        int responseCode = connection.getResponseCode();
        //返回的状态码为-1代表交互失败
        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.");
        }
        //将HTTP协议文本,服务器发回的响应状态代码,状态码的文本描述传入
        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;
    }

接下来是BasicNetwork的performRequest.“重试策略”RetryPolicy便是在这里生效的。可以观察到的是该方法被while(true)包裹起来了。当发生SocketTimeoutException,ConnectTimeoutException,IOException(该错误视情况而重试)时,就会调用attemptRetryOnException(),该方法会调用request.getRetryPolicy(),进而调用RetryPolicy.retry进行重试。一旦重试次数超过设定的次数,就会抛出VolleyError。此外,这里还有部分关于Http状态码的判定,详细可参考:301、404、200、304等HTTP状态

@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());
                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.
                //从HttpEntity中读取数据往byte[]中。其中方法entityToBytes()用的是ByteArrayOutputStream的变体PoolingByteArrayOutputStream,避免了多次分配内存,可自动根据内容大小扩充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;
                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);
                }
            }
        }
    }

下面是介绍下BasicNetworkResponse,这个类主要用来接收数据。

    //HTTP状态码
    public final int statusCode;

    //返回的数据
    public final byte[] data;

    //响应头信息
    public final Map<String, String> headers;

    //当状态码为304时为true
    public final boolean notModified;

    //网络交互总时间
    public final long networkTimeMs;

在NetworkDispatcher中会调用request.parseNetworkResponse(networkResponse)解析数据。举个例子,json解析方法为:

String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers, "utf-8"));

parseCharaset会从响应头中获取解码方式,如果获取失败就会使用utf-8解码。
parseNetworkResponse最终会调用下面这个方法返回Response.第二个参数会将数据解析后放入Cache.Entry中,等下在NetworkDispatcher中会存入硬存中。关于数据的解析需要掌握的是响应头等的参数,详细可参考:Android系列之网络(二)—-HTTP请求头与响应头

Response.success(new JSONObject(jsonString),HttpHeaderParser.parseCacheHeaders(response));

如果有错误就会调用:

Response.error(new ParseError(e));

下面是response的全局变量。它的实例化方法只有两个,就是如上面所述。

    //最终返回的数据类型,当为null时表明有error
    public final T result;

    //缓存元数据
    public final Cache.Entry cacheEntry;

    //错误信息
    public final VolleyError error;

    //当为true时,表明此次得到的数据为soft-expired,第二次刷新得到的数据将很快到达
    public boolean intermediate = false;

到这里为止,数据已经处理完毕。接下来是通过回调将数据在主线程中处理。在RequestQueue接收的参数中有一个是ExecutorDelivery,这个类里面有有一个内部类ResponseDeliveryRunnable。
ExecutorDelivery会将数据封在ResponseDeliveryRunnable中,然后调用handler.post(runnable)在主线程中运行

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

    public ExecutorDelivery(Executor executor) {
        mResponsePoster = executor;
    }

    @Override
    public void postResponse(Request<?> request, Response<?> response) {
        postResponse(request, response, null);
    }

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

    @Override
    public void postError(Request<?> request, VolleyError error) {
        request.addMarker("post-error");
        Response<?> response = Response.error(error);
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
    }
private 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;
        }

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

            //这句代码会在CacheDispatcher中运行,如果缓存已经Soft-expired,将会启动第二次刷新,此时CacheDispatcher将会调用
            /*
            mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {
                                // Not much we can do about this.
                            }
                        }
                    });
            */
            if (mRunnable != null) {
                mRunnable.run();
            }
       }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值