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

来源:互联网 发布:正在安装网络组件特曼 编辑:程序博客网 时间:2024/06/01 22:31

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
原创粉丝点击