BasicNetwork详解

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的一个实例。老规矩,我们看看这个类的注释:

/** A network performing Volley requests over an {@link HttpStack}. */

它实现了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;
}

注释说明,这个是一个实现请求的接口。只有一个方法-performRequest()。一个入参,泛型Reqest。这个方法的作用是实现指定的请求。返回的是请求的结果为-NetworkResponse。

Request

Request是一个抽象类,所有的请求都是继承于这个,作用就是进行网络请求参数的封装和请求结果的解析。在这里暂时不详细介绍。它有三个重要的方法:

    /**
     * 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 parseNetworkResponse(NetworkResponse response)

    /**
     * Mark this request as canceled.
     *
     * 

No callback will be delivered as long as either: * *

    *
  • This method is called on the same thread as the {@link ResponseDelivery} is running on. * By default, this is the main thread. *
  • 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. *
* *

There are no guarantees if both of these conditions aren't met. */ @Override public void cancel()

作用如上的注释。

构造函数

    /**
     * @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.
     *
     * 

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 additionalHeaders) throws IOException, AuthFailureError; }

它是一个接口,类说明是:一个抽象的Http栈。又说了:应该避免使用此接口,因为它取决于已弃用的Apache HTTP库。 使用{@link BaseHttpStack}来避免这种依赖。 此类可能会在未来的Volley版本中删除。这是,因为,谷歌android5.0之后更换了将HttpClient更换成了HttpURLConnection。关于这部分,我们先暂时分析到这里,稍后我们在后面详细讲讲网络请求的实现。

ByteArrayPool

类说明:

/**
 * ByteArrayPool is a source and repository of byte[] 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.
 *
 * 

A good candidate user for this class is something like an I/O system that uses large temporary * byte[] 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. * *

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. * *

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. * *

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[]进行操作,节省I/O,减小内存分配和垃圾回收的系统消耗。提供了两个方法:

  • public synchronized byte[] getBuf(int len)
    如果一个缓冲区在请求的大小中可用,则返回该缓冲区中的缓冲区;如果池中的缓冲区不可用,则返回一个新的缓冲区。
  • public synchronized void returnBuf(byte[] buf)
    返回池的缓冲区,如果池超过其分配的大小,则丢弃旧的缓冲区。

回到构造函数。有两个被废弃的,我们不分析了。另外的两个的区别是,一个不带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以实现向后兼容性,因为它是受保护的字段。 但是,我们不会在这里直接使用它,因此不直接访问它的客户端不需要依赖Apache HTTP。
        mHttpStack = httpStack;
        mPool = pool;
    }

可以知道,现在我们的网络请求都是使用mBaseHttpStack。
然后我们来看看重写的一个方法-performRequest,从名字可以看出来,这个方法用来实现网络请求。代码有点多,额,我也没办法。读吧。

    @Override
    public NetworkResponse performRequest(Request request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            List
responseHeaders = Collections.emptyList(); try { // Gather headers. Map 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
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 getCacheHeaders(Cache.Entry entry) { // If there's no cache entry, we're done. if (entry == null) { return Collections.emptyMap(); } Map 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 convertHeaders(Header[] headers) { Map 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. * *

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

combineHeaders(List
responseHeaders, Entry entry) { // First, create a case-insensitive set of header names from the network // response. Set 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
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 header : entry.responseHeaders.entrySet()) { if (!headerNamesFromNetworkResponse.contains(header.getKey())) { combinedHeaders.add(new Header(header.getKey(), header.getValue())); } } } } return combinedHeaders; }

首先,调用mBaseHttpStack的excuteRequest()方法,得到请求的结果。然后根据响应码,判断是不是304(HttpURLConnection.HTTP_NOT_MODIFIED),如果是,表明使用上一次请求的结果即可。取request中的cacheEntry的值,创建NetworkResponse,并返回。如果不是,读取接下来的数据流。用 inputStreamToBytes() 方法将接下来的数据流转换成byte[](注释:就是在这里,我们使用到了byteArrayPool)。
继续判断状态码,如果是小于200或大于299,则表明出现异常了,直接抛出一个IOException异常。在异常catch代码中,我们创建一个异常的NetworkResponse。如果不是,继续往下走。我们根据状态码和内容,创建一个新的NetworkResponse,并返回。好了,BaseNetwork源码就分析完毕。

你可能感兴趣的:(BasicNetwork详解)