/** com/android/volley/BasicNetwork.java */ /** Reads the contents of HttpEntity into a byte[]. */ privatebyte[] entityToBytes(HttpEntity entity) throws IOException, ServerError { PoolingByteArrayOutputStream bytes = new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength()); byte[] buffer = null; try { InputStream in = entity.getContent(); if (in == null) { thrownew ServerError(); } buffer = mPool.getBuf(1024); int count; while ((count = in.read(buffer)) != -1) { bytes.write(buffer, 0, count); } return bytes.toByteArray(); } finally { try { // Close the InputStream and release the resources by "consuming the content". entity.consumeContent(); } catch (IOException e) { // This can happen if there was an exception above that left the entity in // an invalid state. VolleyLog.v("Error occured when calling consumingContent"); } mPool.returnBuf(buffer); bytes.close(); } }
/** * com/android/volley/PoolingByteArrayOutputStream.java * Ensures there is enough space in the buffer for the given number of additional bytes. */ privatevoidexpand(int i){ /* Can the buffer handle @i more bytes, if not expand it */ if (count + i <= buf.length) { return; } byte[] newbuf = mPool.getBuf((count + i) * 2); System.arraycopy(buf, 0, newbuf, 0, count); mPool.returnBuf(buf); buf = newbuf; }
/** * com/android/volley/RequestQueue.java * Staging area for requests that already have a duplicate request in flight. * * <ul> * <li>containsKey(cacheKey) indicates that there is a request in flight for the given cache * key.</li> * <li>get(cacheKey) returns waiting requests for the given cache key. The in flight request * is <em>not</em> contained in that list. Is null if no requests are staged.</li> * </ul> */ privatefinal Map<String, Queue<Request<?>>> mWaitingRequests = new HashMap<String, Queue<Request<?>>>();
/** * The set of all requests currently being processed by this RequestQueue. A Request * will be in this set if it is waiting in any queue or currently being processed by * any dispatcher. */ privatefinal Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
/** The cache triage queue. */ privatefinal PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<?>>();
/** The queue of requests that are actually going out to the network. */ privatefinal PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?>>();
/** * com/android/volley/toolbox/HttpHeaderParser.java * Extracts a {@link Cache.Entry} from a {@link NetworkResponse}. * * @param response The network response to parse headers from * @return a cache entry for the given response, or null if the response is not cacheable. */ publicstatic Cache.Entry parseCacheHeaders(NetworkResponse response){ long now = System.currentTimeMillis();
Map<String, String> headers = response.headers;
long serverDate = 0; long lastModified = 0; long serverExpires = 0; long softExpire = 0; long finalExpire = 0; long maxAge = 0; long staleWhileRevalidate = 0; boolean hasCacheControl = false; boolean mustRevalidate = false;
// 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); returnnew NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data, entry.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart); } // Handle moved resources if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.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 = newbyte[0]; } ... if (statusCode < 200 || statusCode > 299) { thrownew IOException(); } returnnew 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) { thrownew RuntimeException("Bad URL " + request.getUrl(), e); } catch (IOException e) { ... } } }
响应超时、连接超时、I/O异常、认证错误、资源重定向都需要重试,注意到performRequet方法是一个无限循环,用完重试次数仍然失败则抛出VolleyError, 结束循环。注意到针对几种状态码进行了特别处理: 1)304 Not Modified 表示客户端发送附带条件的请求时,服务器端允许访问资源,但因发送请求未满足条件,直接返回304(服务器端资源未改变,可直接使用客户端未过期的缓存)。 2)301 Moved Permanently, 302 Not Found 永久重定向和临时重定向,表示请求的资源以及分配了新的URI, 通过首部字段Location获取新的资源地址。通过设定请求的跳转地址redirectUrl, 下次重试时直接使用新地址访问资源。 3)204 No Content 表示服务端接受的请求已经重新处理,但返回的响应报文中不含实体的主体部分。在这种情况下返回空内容,客户端不做处理。 4)小于200和大于299的状态码均抛出异常。