BasicNetwork詳解
BasicNetwork詳解
BasicNetwork負責完成網絡請求蚪燕,受NetworkDispatcher類調用羔挡。在NetworkDispatcher的run()方法中初肉,是這樣調用的。
void run(){
processRequest();
}
......
void processRequest(Request<?> request) {
......
// Perform the network request.
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
......
}
......
Network
它是接口Network的一個實例痕寓。老規(guī)矩,我們看看這個類的注釋:
/** A network performing Volley requests over an {@link HttpStack}. */
它實現(xiàn)了Network接口牌捷。我們閱讀一下這個類的源碼好啰。
/** An interface for performing requests. */
public interface Network {
/**
* Performs the specified request.
*
* @param request Request to process
* @return A {@link NetworkResponse} with data and caching metadata; will never be null
* @throws VolleyError on errors
*/
NetworkResponse performRequest(Request<?> request) throws VolleyError;
}
注釋說明,這個是一個實現(xiàn)請求的接口疑务。只有一個方法-performRequest()沾凄。一個入?yún)ⅲ盒蚏eqest知允。這個方法的作用是實現(xiàn)指定的請求撒蟀。返回的是請求的結果為-NetworkResponse。
Request
Request是一個抽象類温鸽,所有的請求都是繼承于這個保屯,作用就是進行網絡請求參數(shù)的封裝和請求結果的解析。在這里暫時不詳細介紹涤垫。它有三個重要的方法:
/**
* Subclasses must implement this to parse the raw network response and return an appropriate
* response type. This method will be called from a worker thread. The response will not be
* delivered if you return null.
*
* @param response Response from the network
* @return The parsed response, or null in the case of an error
*/
@Override
protected void deliverResponse(String response);
/**
* Subclasses must implement this to perform delivery of the parsed response to their listeners.
* The given response is guaranteed to be non-null; responses that fail to parse are not
* delivered.
*
* @param response The parsed response returned by {@link
* #parseNetworkResponse(NetworkResponse)}
*/
@Override
@SuppressWarnings("DefaultCharset")
protected Response<String> parseNetworkResponse(NetworkResponse response)
/**
* Mark this request as canceled.
*
* <p>No callback will be delivered as long as either:
*
* <ul>
* <li>This method is called on the same thread as the {@link ResponseDelivery} is running on.
* By default, this is the main thread.
* <li>The request subclass being used overrides cancel() and ensures that it does not invoke
* the listener in {@link #deliverResponse} after cancel() has been called in a
* thread-safe manner.
* </ul>
*
* <p>There are no guarantees if both of these conditions aren't met.
*/
@Override
public void cancel()
作用如上的注釋姑尺。
構造函數(shù)
/**
* @deprecated Should never have been exposed in the API. This field may be removed in a future
* release of Volley.
*/
@Deprecated protected final HttpStack mHttpStack;
private final BaseHttpStack mBaseHttpStack;
protected final ByteArrayPool mPool;
/**
* @param httpStack HTTP stack to be used
* @deprecated use {@link #BasicNetwork(BaseHttpStack)} instead to avoid depending on Apache
* HTTP. This method may be removed in a future release of Volley.
*/
@Deprecated
public BasicNetwork(HttpStack httpStack) {
// If a pool isn't passed in, then build a small default pool that will give us a lot of
// benefit and not use too much memory.
this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
}
/**
* @param httpStack HTTP stack to be used
* @param pool a buffer pool that improves GC performance in copy operations
* @deprecated use {@link #BasicNetwork(BaseHttpStack, ByteArrayPool)} instead to avoid
* depending on Apache HTTP. This method may be removed in a future release of Volley.
*/
@Deprecated
public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
mHttpStack = httpStack;
mBaseHttpStack = new AdaptedHttpStack(httpStack);
mPool = pool;
}
/** @param httpStack HTTP stack to be used */
public BasicNetwork(BaseHttpStack httpStack) {
// If a pool isn't passed in, then build a small default pool that will give us a lot of
// benefit and not use too much memory.
this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
}
/**
* @param httpStack HTTP stack to be used
* @param pool a buffer pool that improves GC performance in copy operations
*/
public BasicNetwork(BaseHttpStack httpStack, ByteArrayPool pool) {
mBaseHttpStack = httpStack;
// Populate mHttpStack for backwards compatibility, since it is a protected field. However,
// we won't use it directly here, so clients which don't access it directly won't need to
// depend on Apache HTTP.
mHttpStack = httpStack;
mPool = pool;
}
額,貌似蝠猬,遇到了問題切蟋,我們要看看HttpStack和ByteArrayPool是個啥東西。
HttpStack
代碼不長榆芦,我們直接貼出來柄粹。
/**
* An HTTP stack abstraction.
*
* @deprecated This interface should be avoided as it depends on the deprecated Apache HTTP library.
* Use {@link BaseHttpStack} to avoid this dependency. This class may be removed in a future
* release of Volley.
*/
@Deprecated
public interface HttpStack {
/**
* Performs an HTTP request with the given parameters.
*
* <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,
* and the Content-Type header is set to request.getPostBodyContentType().
*
* @param request the request to perform
* @param additionalHeaders additional headers to be sent together with {@link
* Request#getHeaders()}
* @return the HTTP response
*/
HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
}
它是一個接口喘鸟,類說明是:一個抽象的Http棧。又說了:應該避免使用此接口镰惦,因為它取決于已棄用的Apache HTTP庫迷守。 使用{@link BaseHttpStack}來避免這種依賴。 此類可能會在未來的Volley版本中刪除旺入。這是兑凿,因為,谷歌android5.0之后更換了將HttpClient更換成了HttpURLConnection茵瘾。關于這部分礼华,我們先暫時分析到這里,稍后我們在后面詳細講講網絡請求的實現(xiàn)拗秘。
ByteArrayPool
類說明:
/**
* ByteArrayPool is a source and repository of <code>byte[]</code> objects. Its purpose is to supply
* those buffers to consumers who need to use them for a short period of time and then dispose of
* them. Simply creating and disposing such buffers in the conventional manner can considerable heap
* churn and garbage collection delays on Android, which lacks good management of short-lived heap
* objects. It may be advantageous to trade off some memory in the form of a permanently allocated
* pool of buffers in order to gain heap performance improvements; that is what this class does.
*
* <p>A good candidate user for this class is something like an I/O system that uses large temporary
* <code>byte[]</code> buffers to copy data around. In these use cases, often the consumer wants the
* buffer to be a certain minimum size to ensure good performance (e.g. when copying data chunks off
* of a stream), but doesn't mind if the buffer is larger than the minimum. Taking this into account
* and also to maximize the odds of being able to reuse a recycled buffer, this class is free to
* return buffers larger than the requested size. The caller needs to be able to gracefully deal
* with getting buffers any size over the minimum.
*
* <p>If there is not a suitably-sized buffer in its recycling pool when a buffer is requested, this
* class will allocate a new buffer and return it.
*
* <p>This class has no special ownership of buffers it creates; the caller is free to take a buffer
* it receives from this pool, use it permanently, and never return it to the pool; additionally, it
* is not harmful to return to this pool a buffer that was allocated elsewhere, provided there are
* no other lingering references to it.
*
* <p>This class ensures that the total size of the buffers in its recycling pool never exceeds a
* certain byte limit. When a buffer is returned that would cause the pool to exceed the limit,
* least-recently-used buffers are disposed.
*/
真正的代碼不多圣絮,主要注釋很詳細。 大概的意思是它是一個Byte[]池雕旨,作用是能夠更快速的對byte[]進行操作扮匠,節(jié)省I/O,減小內存分配和垃圾回收的系統(tǒng)消耗凡涩。提供了兩個方法:
-
public synchronized byte[] getBuf(int len)
如果一個緩沖區(qū)在請求的大小中可用棒搜,則返回該緩沖區(qū)中的緩沖區(qū);如果池中的緩沖區(qū)不可用,則返回一個新的緩沖區(qū)活箕。 -
public synchronized void returnBuf(byte[] buf)
返回池的緩沖區(qū)力麸,如果池超過其分配的大小,則丟棄舊的緩沖區(qū)育韩。
回到構造函數(shù)克蚂。有兩個被廢棄的,我們不分析了筋讨。另外的兩個的區(qū)別是埃叭,一個不帶ByteArrayPool。我們直接分析復雜的那個悉罕。
/** @param httpStack HTTP stack to be used */
public BasicNetwork(BaseHttpStack httpStack) {
// If a pool isn't passed in, then build a small default pool that will give us a lot of
// benefit and not use too much memory.
this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
}
/**
* @param httpStack HTTP stack to be used
* @param pool a buffer pool that improves GC performance in copy operations
*/
public BasicNetwork(BaseHttpStack httpStack, ByteArrayPool pool) {
mBaseHttpStack = httpStack;
// Populate mHttpStack for backwards compatibility, since it is a protected field. However,
// we won't use it directly here, so clients which don't access it directly won't need to
// depend on Apache HTTP.
//填充mHttpStack以實現(xiàn)向后兼容性游盲,因為它是受保護的字段。 但是蛮粮,我們不會在這里直接使用它,因此不直接訪問它的客戶端不需要依賴Apache HTTP谜慌。
mHttpStack = httpStack;
mPool = pool;
}
可以知道然想,現(xiàn)在我們的網絡請求都是使用mBaseHttpStack。
然后我們來看看重寫的一個方法-performRequest欣范,從名字可以看出來变泄,這個方法用來實現(xiàn)網絡請求令哟。代碼有點多,額妨蛹,我也沒辦法屏富。讀吧。
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
List<Header> responseHeaders = Collections.emptyList();
try {
// Gather headers.
Map<String, String> additionalRequestHeaders =
getCacheHeaders(request.getCacheEntry());
httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);
int statusCode = httpResponse.getStatusCode();
responseHeaders = httpResponse.getHeaders();
// Handle cache validation.
if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(
HttpURLConnection.HTTP_NOT_MODIFIED,
/* data= */ null,
/* notModified= */ true,
SystemClock.elapsedRealtime() - requestStart,
responseHeaders);
}
// Combine cached and response headers so the response will be complete.
List<Header> combinedHeaders = combineHeaders(responseHeaders, entry);
return new NetworkResponse(
HttpURLConnection.HTTP_NOT_MODIFIED,
entry.data,
/* notModified= */ true,
SystemClock.elapsedRealtime() - requestStart,
combinedHeaders);
}
// Some responses such as 204s do not have content. We must check.
InputStream inputStream = httpResponse.getContent();
if (inputStream != null) {
responseContents =
inputStreamToBytes(inputStream, httpResponse.getContentLength());
} 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, statusCode);
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
return new NetworkResponse(
statusCode,
responseContents,
/* notModified= */ false,
SystemClock.elapsedRealtime() - requestStart,
responseHeaders);
} catch (SocketTimeoutException e) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (MalformedURLException e) {
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
int statusCode;
if (httpResponse != null) {
statusCode = httpResponse.getStatusCode();
} else {
throw new NoConnectionError(e);
}
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
NetworkResponse networkResponse;
if (responseContents != null) {
networkResponse =
new NetworkResponse(
statusCode,
responseContents,
/* notModified= */ false,
SystemClock.elapsedRealtime() - requestStart,
responseHeaders);
if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED
|| statusCode == HttpURLConnection.HTTP_FORBIDDEN) {
attemptRetryOnException(
"auth", request, new AuthFailureError(networkResponse));
} else if (statusCode >= 400 && statusCode <= 499) {
// Don't retry other client errors.
throw new ClientError(networkResponse);
} else if (statusCode >= 500 && statusCode <= 599) {
if (request.shouldRetryServerErrors()) {
attemptRetryOnException(
"server", request, new ServerError(networkResponse));
} else {
throw new ServerError(networkResponse);
}
} else {
// 3xx? No reason to retry.
throw new ServerError(networkResponse);
}
} else {
attemptRetryOnException("network", request, new NetworkError());
}
}
}
}
/** Logs requests that took over SLOW_REQUEST_THRESHOLD_MS to complete. */
private void logSlowRequests(
long requestLifetime, Request<?> request, byte[] responseContents, int statusCode) {
if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) {
VolleyLog.d(
"HTTP response for request=<%s> [lifetime=%d], [size=%s], "
+ "[rc=%d], [retryCount=%s]",
request,
requestLifetime,
responseContents != null ? responseContents.length : "null",
statusCode,
request.getRetryPolicy().getCurrentRetryCount());
}
}
/**
* Attempts to prepare the request for a retry. If there are no more attempts remaining in the
* request's retry policy, a timeout exception is thrown.
*
* @param request The request to use.
*/
private static void attemptRetryOnException(
String logPrefix, Request<?> request, VolleyError exception) throws VolleyError {
RetryPolicy retryPolicy = request.getRetryPolicy();
int oldTimeout = request.getTimeoutMs();
try {
retryPolicy.retry(exception);
} catch (VolleyError e) {
request.addMarker(
String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
throw e;
}
request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
}
private Map<String, String> getCacheHeaders(Cache.Entry entry) {
// If there's no cache entry, we're done.
if (entry == null) {
return Collections.emptyMap();
}
Map<String, String> headers = new HashMap<>();
if (entry.etag != null) {
headers.put("If-None-Match", entry.etag);
}
if (entry.lastModified > 0) {
headers.put(
"If-Modified-Since", HttpHeaderParser.formatEpochAsRfc1123(entry.lastModified));
}
return headers;
}
protected void logError(String what, String url, long start) {
long now = SystemClock.elapsedRealtime();
VolleyLog.v("HTTP ERROR(%s) %d ms to fetch %s", what, (now - start), url);
}
/** Reads the contents of an InputStream into a byte[]. */
private byte[] inputStreamToBytes(InputStream in, int contentLength)
throws IOException, ServerError {
PoolingByteArrayOutputStream bytes = new PoolingByteArrayOutputStream(mPool, contentLength);
byte[] buffer = null;
try {
if (in == null) {
throw new 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".
if (in != null) {
in.close();
}
} catch (IOException e) {
// This can happen if there was an exception above that left the stream in
// an invalid state.
VolleyLog.v("Error occurred when closing InputStream");
}
mPool.returnBuf(buffer);
bytes.close();
}
}
/**
* Converts Headers[] to Map<String, String>.
*
* @deprecated Should never have been exposed in the API. This method may be removed in a future
* release of Volley.
*/
@Deprecated
protected static Map<String, String> convertHeaders(Header[] headers) {
Map<String, String> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
for (int i = 0; i < headers.length; i++) {
result.put(headers[i].getName(), headers[i].getValue());
}
return result;
}
/**
* Combine cache headers with network response headers for an HTTP 304 response.
*
* <p>An 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. See also:
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
*
* @param responseHeaders Headers from the network response.
* @param entry The cached response.
* @return The combined list of headers.
*/
private static List<Header> combineHeaders(List<Header> responseHeaders, Entry entry) {
// First, create a case-insensitive set of header names from the network
// response.
Set<String> headerNamesFromNetworkResponse = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
if (!responseHeaders.isEmpty()) {
for (Header header : responseHeaders) {
headerNamesFromNetworkResponse.add(header.getName());
}
}
// Second, add headers from the cache entry to the network response as long as
// they didn't appear in the network response, which should take precedence.
List<Header> combinedHeaders = new ArrayList<>(responseHeaders);
if (entry.allResponseHeaders != null) {
if (!entry.allResponseHeaders.isEmpty()) {
for (Header header : entry.allResponseHeaders) {
if (!headerNamesFromNetworkResponse.contains(header.getName())) {
combinedHeaders.add(header);
}
}
}
} else {
// Legacy caches only have entry.responseHeaders.
if (!entry.responseHeaders.isEmpty()) {
for (Map.Entry<String, String> header : entry.responseHeaders.entrySet()) {
if (!headerNamesFromNetworkResponse.contains(header.getKey())) {
combinedHeaders.add(new Header(header.getKey(), header.getValue()));
}
}
}
}
return combinedHeaders;
}
首先蛙卤,調用mBaseHttpStack的excuteRequest()方法狠半,得到請求的結果。然后根據(jù)響應碼颤难,判斷是不是304(HttpURLConnection.HTTP_NOT_MODIFIED)神年,如果是,表明使用上一次請求的結果即可行嗤。取request中的cacheEntry的值已日,創(chuàng)建NetworkResponse,并返回栅屏。如果不是飘千,讀取接下來的數(shù)據(jù)流。用 inputStreamToBytes() 方法將接下來的數(shù)據(jù)流轉換成byte[](注釋:就是在這里栈雳,我們使用到了byteArrayPool)护奈。
繼續(xù)判斷狀態(tài)碼,如果是小于200或大于299甫恩,則表明出現(xiàn)異常了逆济,直接拋出一個IOException異常。在異常catch代碼中磺箕,我們創(chuàng)建一個異常的NetworkResponse奖慌。如果不是,繼續(xù)往下走松靡。我們根據(jù)狀態(tài)碼和內容简僧,創(chuàng)建一個新的NetworkResponse,并返回雕欺。好了岛马,BaseNetwork源碼就分析完畢。