Volley Manager 不能发送Header的Bug
2016-01-14 14:40
288 查看
项目中使用了Volley Manger这个库,是对Volley又封装了一层,实现了一个通用的ByteArrayRequest,可以用来发送各种类型的请求,也可以上传文件。
只有很少的9个类,却大大简化了Volley的使用。
但是在与服务器端保持Session的时候,出现了问题,Session一直保持不了。
查看运行日志,获取SessionID和发送SessionID时是都正确,就是多次登录服务器会返回多个不同的SessionID,于是怀疑sessionid根本就没有发送给服务器端。Volley是个通用的网络请求库,问题应该不大,于是怀疑,Volley Manager在使用Volley时可能不当,导致header没有发送出去。
抓包验证了上述结论,自己填充的Cookie的header确实没有发送出去。
之前读过Volley的一部分源码,问题定位到Volley Manager自定义的ByteArrayRequest#getHeaders()
其实现如下:
于是修改ByteArrayRequest类的相关部分实现。
增加下面的域定义:
为什么修改XXXRequest的getHeaders方法可以?
原因如下:
Volley里面的RequestQueue是Volley的最最核心的类,RequestQueue可以将Volley所有的类串起来。
其内部保持了一个NetworkDispatcher的数组,NetworkDispatcher继承自Thread类,是一个线程,用于调度处理走网络的请求。启动后会不断从网络请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。他跟加到RequestQueue中的Request对象一一对应。
NetworkDispatcher的run方法如下:
具体看上边的注解就可以了。最最核心的一行代码牵扯到一个Network对象。要看Network的作用,可以看一下Volley提供的Network的基本实现BasicNetwork类。
BasicNetwork#performRequest的实现如下:
可以看出,真正执行请求的不是BasicNetwork而是调用了HttpStack的performRequest方法。BasicNetwork只是在请求之前处理了一下请求头,请求响应之后,处理一下响应的结果。将请求结果转换为可以被ResponseDelivery处理的NetworkResponse对象。
下面看一下HttpStack,HttpStack是一个接口,Volley提供了一个java.net.HttpUrlConnection的实现HurlStack。
下面看一下HurlStack#performRequest方法的实现:
可以看上边重点标记的部分:
源码解析到这里,原因就可以解释了。Volley框架是通过Request#getHeaders()方法获取用户放置在header的信息的,而Volley Manager自定义的ByteArrayRequest的getHeaders方法,每次都返回一个空的HashMap,所以开发者放置的头信息,Volley无法获取到。
只有很少的9个类,却大大简化了Volley的使用。
但是在与服务器端保持Session的时候,出现了问题,Session一直保持不了。
查看运行日志,获取SessionID和发送SessionID时是都正确,就是多次登录服务器会返回多个不同的SessionID,于是怀疑sessionid根本就没有发送给服务器端。Volley是个通用的网络请求库,问题应该不大,于是怀疑,Volley Manager在使用Volley时可能不当,导致header没有发送出去。
抓包验证了上述结论,自己填充的Cookie的header确实没有发送出去。
之前读过Volley的一部分源码,问题定位到Volley Manager自定义的ByteArrayRequest#getHeaders()
其实现如下:
@Override public Map<String, String> getHeaders() throws AuthFailureError { Map<String, String> headers = super.getHeaders(); if (null == headers || headers.equals(Collections.emptyMap())) { headers = new HashMap<String, String>(); } return headers; }查看super.getHeaders()的实现:
public Map<String, String> getHeaders() throws AuthFailureError { <span style="white-space:pre"> </span>return Collections.emptyMap(); }问题出现了,当Volley框架调用xxxRequest的getHeaders方法时,肯定是个空的HashMap。
于是修改ByteArrayRequest类的相关部分实现。
增加下面的域定义:
private Map<String,String> mHeaders = new HashMap<String, String>();修改getHeaders方法如下:
public Map<String, String> getHeaders() throws AuthFailureError { <span style="white-space:pre"> </span>return this.mHeaders;}
为什么修改XXXRequest的getHeaders方法可以?
原因如下:
Volley里面的RequestQueue是Volley的最最核心的类,RequestQueue可以将Volley所有的类串起来。
其内部保持了一个NetworkDispatcher的数组,NetworkDispatcher继承自Thread类,是一个线程,用于调度处理走网络的请求。启动后会不断从网络请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。他跟加到RequestQueue中的Request对象一一对应。
NetworkDispatcher的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对象
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"); // If the request was cancelled already, do not perform the // network request. if (request.isCanceled()) { request.finish("network-discard-cancelled"); continue; } addTrafficStatsTag(request); // 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; } // Parse the response here on the worker thread.解析请求的响应 Response<?> response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete"); // 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); } } }
具体看上边的注解就可以了。最最核心的一行代码牵扯到一个Network对象。要看Network的作用,可以看一下Volley提供的Network的基本实现BasicNetwork类。
BasicNetwork#performRequest的实现如下:
@Override public NetworkResponse performRequest(Request<?> request) throws VolleyError { long requestStart = SystemClock.elapsedRealtime(); while (true) { HttpResponse httpResponse = null; byte[] responseContents = null; Map<String, String> responseHeaders = new HashMap<String, String>(); try { // Gather headers. Map<String, String> headers = new HashMap<String, String>(); addCacheHeaders(headers, request.getCacheEntry()); httpResponse = mHttpStack.performRequest(request, headers); responseHeaders.putAll(httpResponse.getAllHeaders()); int statusCode = httpResponse.getResponseCode(); // Handle cache validation. if (statusCode == HttpResponse.SC_NOT_MODIFIED) { Entry entry = request.getCacheEntry(); if (entry == null) { return new NetworkResponse( HttpResponse.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(HttpResponse.SC_NOT_MODIFIED, entry.data, entry.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart); } // Handle moved resources if (statusCode == HttpResponse.SC_MOVED_PERMANENTLY || statusCode == HttpResponse.SC_MOVED_TEMPORARILY) { String newUrl = responseHeaders.get("Location"); request.setRedirectUrl(newUrl); } // 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, httpResponse); 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 (MalformedURLException e) { throw new RuntimeException("Bad URL " + request.getUrl(), e); } catch (IOException e) { int statusCode = 0; NetworkResponse networkResponse = null; if (httpResponse != null) { statusCode = httpResponse.getResponseCode(); } else { throw new NoConnectionError(e); } if (statusCode == HttpResponse.SC_MOVED_PERMANENTLY || statusCode == HttpResponse.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 == HttpResponse.SC_UNAUTHORIZED || statusCode == HttpResponse.SC_FORBIDDEN) { attemptRetryOnException("auth", request, new AuthFailureError(networkResponse)); } else if (statusCode == HttpResponse.SC_MOVED_PERMANENTLY || statusCode == HttpResponse.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); } } } }
可以看出,真正执行请求的不是BasicNetwork而是调用了HttpStack的performRequest方法。BasicNetwork只是在请求之前处理了一下请求头,请求响应之后,处理一下响应的结果。将请求结果转换为可以被ResponseDelivery处理的NetworkResponse对象。
下面看一下HttpStack,HttpStack是一个接口,Volley提供了一个java.net.HttpUrlConnection的实现HurlStack。
下面看一下HurlStack#performRequest方法的实现:
@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 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. 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."); } HttpResponse response = new HttpResponse(connection.getResponseCode(), connection.getResponseMessage()); response.setEntity(entityFromConnection(connection)); for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) { if (header.getKey() != null) { response.addHeader(header.getKey(), header.getValue().get(0)); } } return response; }
可以看上边重点标记的部分:
map.putAll(request.getHeaders()); map.putAll(additionalHeaders);第一行调用XXXRequest.getHeaders()获取Request对象的headers,第二行是获取在Network的实现里处理的请求头,比如BasicNetwork里是将Cache相关的头信息传递给了HttpStack#performRequest方法的第二个参数【主要是if-none-match,if-modified-since】。
源码解析到这里,原因就可以解释了。Volley框架是通过Request#getHeaders()方法获取用户放置在header的信息的,而Volley Manager自定义的ByteArrayRequest的getHeaders方法,每次都返回一个空的HashMap,所以开发者放置的头信息,Volley无法获取到。
相关文章推荐
- 创建子分区表 partition by subpartition by
- Android 剪贴板操作方法在不同版本API下的使用
- spring aop 2
- 第十二章 对象(上):
- IE 中使用 Console
- iOS中属性与成员变量的区别
- iOS 检测更新
- 做为一个员工及团队成员应具备的基本素质(团队成员应该具备的一大素质(从主管的角度出发))
- ueditor使用中的问题总结
- DialogFragment详解
- 高效使用Bitmaps(一) 大Bitmap的加载
- 接口类和抽象类的区别
- 十个调试技巧
- 学习方法总结
- 【总结】八大排序算法
- Logstash学习7_Logstash_Date时间处理_Joda-Time库
- Shallow Size、Retained Size、Heap Size和Allocated
- 基于HttpClient实现网络爬虫~以百度新闻为例
- Swift UITableView
- TCP拥塞控制