关于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(); } } }}
- 关于volley的一些分析-发送与接收
- 关于volley的一些分析-初始化
- android中关于udp发送、接收的一些重点总结
- 深入分析JavaWeb Item41 -- 邮件的发送与接收原理
- 深入分析JavaWeb 41 -- 邮件的发送与接收原理
- 广播的发送与接收
- 多播包的发送与接收
- 广播的发送与接收
- 广播的发送与接收
- PDU短信发送与接收格式分析
- 发送/接收数据包与发送/接收字节的区别.
- 驱动的发送接收流程分析
- 关于Volley二次封装的一些想法
- 关于VC++中,两种自定义消息的发送与接收的方法实现进行说明。
- 关于VC++中,两种自定义消息的发送与接收的方法实现进行说明。
- 关于VC++中,两种自定义消息的发送与接收的方法实现进行说明
- 关于VC++中,两种自定义消息的发送与接收的方法实现进行说明。
- 关于VC++中,两种自定义消息的发送与接收的方法实现进行说明
- RunTime.getRunTime().addShutdownHook用法
- ubuntu中玩caffe里面的 MNIST
- android Graphics(四):canvas变换与操作
- 水仙花数
- Light OJ 1036A Refining Company (DP)
- 关于volley的一些分析-发送与接收
- 曹珍富老师的成材树
- android Graphics( 五):drawText()详解
- docker入门绝佳资源
- 递归删除目录下所有文件
- Linux中profile、bashrc、bash_profile之间的区别和联系
- 基础知识(十)C++常用函数.txt
- 欢迎使用CSDN-markdown编辑器
- Windows Sublime Text 3 个人使用相关技巧