Volley 源码解析: 一款 Google 推出的网络请求框架

本篇源码解析基于 Volley 1.1.1

个人博客:https://blog.N0tExpectErr0r.cn

小专栏:https://xiaozhuanlan.com/N0tExpectErr0r

Volley 是 Google 开发的一款网络请求框架,目前已停止更新。虽然目前大家的关注焦点都在 Retrofit、OkHttp 等第三方网络请求框架,团队的项目中所用的也是这两个框架,但 Volley 中还是有非常多优秀的设计思想值得我们去学习的。因此今天准备来学习一下 Volley 的源码,了解一下它的核心设计思想。

Volley

我们先看到 Volley 的入口——Volley 类。

创建 RequestQueue

Volley 在使用之前,我们需要一个请求队列对象 RequestQueue,一般整个应用统一用同一个 RequestQueue,让我们看看创建它的方法 Volley.newRequestQueue(Context)

/**
 * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
 *
 * @param context A {@link Context} to use for creating the cache dir.
 * @return A started {@link RequestQueue} instance.
 */
public static RequestQueue newRequestQueue(Context context) {
    return newRequestQueue(context, (BaseHttpStack) null);
}

它转调到了 Volley.newRequestQueue(Context, BaseHttpStack) 方法,同时还有一个 Volley.newRequestQueue(Context, HttpStack) 方法:

/**
 * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
 *
 * @param context A {@link Context} to use for creating the cache dir.
 * @param stack An {@link HttpStack} to use for the network, or null for default.
 * @return A started {@link RequestQueue} instance.
 * @deprecated Use {@link #newRequestQueue(Context, BaseHttpStack)} instead to avoid depending
 *     on Apache HTTP. This method may be removed in a future release of Volley.
 */
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
    if (stack == null) {
        return newRequestQueue(context, (BaseHttpStack) null);
    }
    return newRequestQueue(context, new BasicNetwork(stack));
}

从上面的注释可以看出,HttpStack 是一个用于在 NetWork 中使用的对象,如果传入的 stack 不为 null,则会调用到 Volley.newRequestQueue(Context, Network),否则它同样会转调到 Volley.newRequestQueue(Context, BaseHttpStack)

/**
 * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
 *
 * @param context A {@link Context} to use for creating the cache dir.
 * @param stack A {@link BaseHttpStack} to use for the network, or null for default.
 * @return A started {@link RequestQueue} instance.
 */
public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {
    BasicNetwork network;
    // 创建 NetWork 对象
    if (stack == null) {
   			// stack 为空则创建一个Stack,然后再创建 NetWork
    		// 高版本下,HttpStack 使用的是 HurlStack,低版本下使用 HttpClientStack
        if (Build.VERSION.SDK_INT >= 9) {
            network = new BasicNetwork(new HurlStack());
        } else {
            // Prior to Gingerbread, HttpUrlConnection was unreliable.
            // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
            // At some point in the future we'll move our minSdkVersion past Froyo and can
            // delete this fallback (along with all Apache HTTP code).
            String userAgent = "volley/0";
            try {
                String packageName = context.getPackageName();
                PackageInfo info =
                        context.getPackageManager().getPackageInfo(packageName, /* flags= */ 0);
                userAgent = packageName + "/" + info.versionCode;
            } catch (NameNotFoundException e) {
            }
            network =
                    new BasicNetwork(
                            new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));
        }
    } else {
        network = new BasicNetwork(stack);
    }
    return newRequestQueue(context, network);
}

这个方法主要做的事情就是根据 stack 创建 netWork 对象。

stack 为空时,对于高于 9 的 SDK 版本,使用 HurlStack,而对于低于它的版本则使用 HttpClientStack。根据上面的注释可以看出这样做的原因:因为在 SDK 9 之前的 HttpUrlConnection 不是很可靠。(我们可以推测在高版本 (SDK > 9)Volley 基于 HttpUrlConnection 实现,低版本则基于 HttpClient 实现。

另外这里可能会比较疑惑,NetWorkHttpStack 都是用来做什么的?这些问题我们后面都会一一解决。

它最后同样调用到了 Volley.newRequestQueue(Context, Network)

private static RequestQueue newRequestQueue(Context context, Network network) {
    final Context appContext = context.getApplicationContext();
    // Use a lazy supplier for the cache directory so that newRequestQueue() can be called on
    // main thread without causing strict mode violation.
    DiskBasedCache.FileSupplier cacheSupplier =
            new DiskBasedCache.FileSupplier() {
                private File cacheDir = null;
                @Override
                public File get() {
                    if (cacheDir == null) {
                        cacheDir = new File(appContext.getCacheDir(), DEFAULT_CACHE_DIR);
                    }
                    return cacheDir;
                }
            };
    RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheSupplier), network);
    queue.start();
    return queue;
}

首先根据 Context 获取到了应用缓存文件夹并创建 Cache 文件。这里有个比较小的细节,为了在 Application 调用 newRequestQueue 同时又不被 StrictMode 有关文件操作相关的规则所影响,Volley 中使用了一个 FileSupplier 来对 File 进行包装,它采用了一个懒创建的思路,只有用到的时候才创建对应的 cacheDir

之后构造了 RequestQueue,调用了其 start 方法并对其返回。可以看出来,Volley 是一个 RequestQueue 的静态工厂。

RequestQueue

RequestQueue 中维护了三个容器:两个 PriorityBlockingQueuemCacheQueuemNetQueue,以及一个 SetmCurrentRequests

  • mCacheQueue:用于存放缓存的待请求的 Request
  • mNetQueue :用于存放等待发起的 Request
  • mCurrentQueue:用于存放当前正在进行请求的 Request

创建

我们先来看看 RequestQueue 的构造函数:

public RequestQueue(Cache cache, Network network) {
    this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}

它转调到了另一个构造函数,并传递了一个默认线程数 DEFAULT_NETWORK_THREAD_POOL_SIZE,它代表了网络请求分派线程启动时的默认个数,默认值为 4。

public RequestQueue(Cache cache, Network network, int threadPoolSize) {
    this(
            cache,
            network,
            threadPoolSize,
            new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}

这个构造函数创建了一个主线程的 Handler 并用它构建了一个 ExecutorDelivery,关于 ExecutorDelivery 我们后面再讨论,它是一个用于交付 Response 和 Error 信息的类。

后面转调的构造函数主要是进行一些赋值。

启动

接着我们看看 RequestQueue.start,看看它是如何启动的:

/** Starts the dispatchers in this queue. */
public void start() {
    stop(); // Make sure any currently running dispatchers are stopped.
    // Create the cache dispatcher and start it.
    mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
    mCacheDispatcher.start();
    // Create network dispatchers (and corresponding threads) up to the pool size.
    for (int i = 0; i < mDispatchers.length; i++) {
        NetworkDispatcher networkDispatcher =
                new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
        mDispatchers[i] = networkDispatcher;
        networkDispatcher.start();
    }
}

start 的主要用途是启动该 Queue 中的 Dispatcher。它的主要步骤如下:

  1. 调用 stop 方法停止所有正在运行的 Dispatcher
  2. 创建 CacheDispatcher 并启动。
  3. 创建前面指定个数的 NetworkDispatcher(默认为 4 个)并启动。

可以看出来,每个 RequestQueue 中共有 5 个 Dispatcher,其中有 4 个 NetworkDispatcher 和 1 个 CacheDispatcher

入队

我们可以通过 RequestQueue.add 将一个 Request 入队,它会根据当前 Request 是否需要进行缓存将其加入 mNetworkQueuemCacheQueue(这里实际上 GET 请求首先会放入 mCacheQueue,其余请求直接放入 mNetworkQueue

/**
 * Adds a Request to the dispatch queue.
 *
 * @param request The request to service
 * @return The passed-in request
 */
public <T> Request<T> add(Request<T> request) {
    // Tag the request as belonging to this queue and add it to the set of current requests.
    request.setRequestQueue(this);
    synchronized (mCurrentRequests) {
        mCurrentRequests.add(request);
    }
    // Process requests in the order they are added.
    request.setSequence(getSequenceNumber());
    request.addMarker("add-to-queue");
    sendRequestEvent(request, RequestEvent.REQUEST_QUEUED);
    // If the request is uncacheable, skip the cache queue and go straight to the network.
    if (!request.shouldCache()) {
        mNetworkQueue.add(request);
        return request;
    }
    mCacheQueue.add(request);
    return request;
}

停止

我们先看看 stop 做了什么:

public void stop() {
    if (mCacheDispatcher != null) {
        mCacheDispatcher.quit();
    }
    for (final NetworkDispatcher mDispatcher : mDispatchers) {
        if (mDispatcher != null) {
            mDispatcher.quit();
        }
    }
}

这里仅仅是对每个 Dispatcher 调用了其 quit 方法。

结束

RequestQueue 还有个 finish 方法,对应了 Request.finish

/**
 * Called from {@link Request#finish(String)}, indicating that processing of the given request
 * has finished.
 */
@SuppressWarnings("unchecked") // see above note on RequestFinishedListener
<T> void finish(Request<T> request) {
    // Remove from the set of requests currently being processed.
    synchronized (mCurrentRequests) {
        mCurrentRequests.remove(request);
    }
    synchronized (mFinishedListeners) {
        for (RequestFinishedListener<T> listener : mFinishedListeners) {
            listener.onRequestFinished(request);
        }
    }
    sendRequestEvent(request, RequestEvent.REQUEST_FINISHED);
}

主要是将结束的 RequestmCurrentRequests 中移除,并调用外部注册的回调以及发送 REQUEST_FINISHED 事件。

ExecutorDelivery

接下来我们看看 ExecutorDelivery 究竟是做什么的

/** Delivers responses and errors. */
public class ExecutorDelivery implements ResponseDelivery {
		private final Executor mResponsePoster;

    /**
     * Creates a new response delivery interface.
     *
     * @param handler {@link Handler} to post responses on
     */
    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);
                    }
                };
    }
    
    //...
}

根据上面的注释可以看出 ExecutorDelivery 的主要作用是交付 Response 和 Error 信息。

它内部持有了一个名为 ResponsePosterExecutor,每个调用这个 Posterexecute 方法的 Runnable 都会通过 Handler.post 发送到主线程的 MessageQueue 中。

接着我们看到它内部的方法:

@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));
}

可以看出来,它内部的方法主要是将 Response 信息和 Error 信息 post 到 MessageQueue 中,其中 requestresponse 会被包装为一个 ResponseDeliveryRunnable

ResponseDeliveryRunnable

ResponseDeliveryRunnableExecutorDelivery 的一个内部类,可以看到它的 run 方法:

@Override
public void run() {
    // NOTE: If cancel() is called off the thread that we're currently running in (by
    // default, the main thread), we cannot guarantee that deliverResponse()/deliverError()
    // won't be called, since it may be canceled after we check isCanceled() but before we
    // deliver the response. Apps concerned about this guarantee must either call cancel()
    // from the same thread or implement their own guarantee about not invoking their
    // listener after cancel() has been called.
    // 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");
    }
    // If we have been provided a post-delivery runnable, run it.
    if (mRunnable != null) {
        mRunnable.run();
    }
}

这里会根据 RequestResponse 的不同状态调用 Request 中的不同方法以对 Response 和 Error 的结果进行交付:

  • 如果 Request 被取消,调用 Request.finish 方法直接结束。
  • 如果请求成功,调用 Request.deliverResponse 方法反馈 Response
  • 如果请求失败,调用 Request.deliverError 方法反馈 Error
  • 如果该请求只是一个中间请求,则不结束该请求,而是给它加上一个 “intermediate-response” 标记,否则结束该请求。
  • 如果 postResponse 方法传递了一个 Runnbale 进来,则执行该 Runnable

NetworkDispatcher

我们接着来看到 NetworkDispatcher,它继承自 Thread,我们先看到其 run 方法:

@Override
public void run() {
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    while (true) {
        try {
            processRequest();
        } catch (InterruptedException e) {
            // We may have been interrupted because it was time to quit.
            if (mQuit) {
                Thread.currentThread().interrupt();
                return;
            }
            VolleyLog.e(
                    "Ignoring spurious interrupt of NetworkDispatcher thread; "
                            + "use quit() to terminate it");
        }
    }
}

它在不断循环调用 processRequest 方法:

// Extracted to its own method to ensure locals have a constrained liveness scope by the GC.
// This is needed to avoid keeping previous request references alive for an indeterminate amount
// of time. Update consumer-proguard-rules.pro when modifying this. See also
// https://github.com/google/volley/issues/114
private void processRequest() throws InterruptedException {
    // Take a request from the queue.
    Request<?> request = mQueue.take();
    processRequest(request);
}

这里首先从 mQuqueRequestQueue 中的 NetQuque)中取出了 Request,虽然 NetworkDispacher 是多个同时执行,但由于使用了 BlockingQueue 因此不用考虑线程安全问题。

可以发现这是一种典型的生产者消费者模型,多个 NetworkDispatcher 不断地从 NetQueue 中取出 Request 并进行网络请求,用户就是网络请求的生产者,而 NetworkDispatcher 就是网络请求的消费者。

processRequest 方法继续调用了 processRequest(Request) 方法:

void processRequest(Request<?> request) {
    long startTimeMs = SystemClock.elapsedRealtime();
    // 发送 REQUEST_NETWORK_DISPATCH_STARTED 事件
    request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_STARTED);
    try {
        request.addMarker("network-queue-take");
        // 如果这个 request 已经被取消,则 finish 它并通知 Listener Response 没有用
        if (request.isCanceled()) {
            request.finish("network-discard-cancelled");
            request.notifyListenerResponseNotUsable();
            return;
        }
        addTrafficStatsTag(request);
        // 调用 mNetwork.performRequest 发起同步请求拿到 Response
        NetworkResponse networkResponse = mNetwork.performRequest(request);
        request.addMarker("network-http-complete");
        // 如果返回了 304 并且我们已经交付了 Reponse,则 finish 它并通知 Listener Response 没有用
        if (networkResponse.notModified && request.hasHadResponseDelivered()) {
            request.finish("not-modified");
            request.notifyListenerResponseNotUsable();
            return;
        }
        // 对 response 进行转换
        Response<?> response = request.parseNetworkResponse(networkResponse);
        request.addMarker("network-parse-complete");
        // 如果适用,写入缓存
        if (request.shouldCache() && response.cacheEntry != null) {
            mCache.put(request.getCacheKey(), response.cacheEntry);
            request.addMarker("network-cache-written");
        }
        // 通过 ExecutorDelivery 对 Response 进行交付并将 request 标记为已交付
        request.markDelivered();
        mDelivery.postResponse(request, response);
        // 通知 Listener 已获得 Response
        request.notifyListenerResponseReceived(response);
    } catch (VolleyError volleyError) {
        volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
        // 交付 Error 信息并通知 Listener Response 没有用
        parseAndDeliverNetworkError(request, volleyError);
        request.notifyListenerResponseNotUsable();
    } catch (Exception e) {
        VolleyLog.e(e, "Unhandled exception %s", e.toString());
        VolleyError volleyError = new VolleyError(e);
        volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
        // 交付 Error 信息并通知 Listener Response 没有用
        mDelivery.postError(request, volleyError);
        request.notifyListenerResponseNotUsable();
    } finally {
    		// 发送 REQUEST_NETWORK_DISPATCH_FINISHED 事件
        request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_FINISHED);
    }
}

上面的代码比较长,我们可以将其分为如下的步骤:

  1. 发送 REQUEST_NETWORK_DISPATCH_STARTED 事件,表示请求已开始
  2. 如果 request 已经被取消,则 finish 它并通知 Listener Response 没有用,不再继续进行
  3. 调用 mNetwork.performRequest 发起同步请求拿到 Response
  4. 如果返回了 304 并且我们已经交付了 Reponse,则 finish 它并通知 Listener Response 没有用,不再继续进行
  5. Response 进行转换
  6. 如果该 Request 可以进行缓存,以 Request 为 key,Response 为 value 进行缓存
  7. 通过 ExecutorDeliveryResponse 进行交付并将 Request 标记为已交付
  8. 通知 Listener 已获得 Response
  9. 如果出现了异常,交付 Error 信息并通知 Listener Response 没有用,其中所有 Error 都需要转换成 VollyError 类。
  10. 发送 REQUEST_NETWORK_DISPATCH_FINISHED 事件,表示请求已结束

从上面的步骤我们可以得到一些信息:

  1. 有一套 Event 机制用于传送各种事件
  2. Network 类是网络请求真正的实现类。
  3. Response 有个转换的问题
  4. 存在一套 Response 缓存机制。

CacheDispatcher

CacheDispatcher 同样继承自 Thread,我们先看到其 run 方法:

@Override
public void run() {
    if (DEBUG) VolleyLog.v("start new dispatcher");
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    // Make a blocking call to initialize the cache.
    mCache.initialize();
    while (true) {
        try {
            processRequest();
        } catch (InterruptedException e) {
            // We may have been interrupted because it was time to quit.
            if (mQuit) {
                Thread.currentThread().interrupt();
                return;
            }
            VolleyLog.e(
                    "Ignoring spurious interrupt of CacheDispatcher thread; "
                            + "use quit() to terminate it");
        }
    }
}

它也是在不断地调用 processRequest 方法:

private void processRequest() throws InterruptedException {
    // Get a request from the cache triage queue, blocking until
    // at least one is available.
    final Request<?> request = mCacheQueue.take();
    processRequest(request);
}

这里与 NetworkDispatcher 中一样,从 mCacheQueue 中取出待请求的 Request,之后看到 processRequest(Request)

void processRequest(final Request<?> request) throws InterruptedException {
    request.addMarker("cache-queue-take");
    // 发送 Event
    request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_STARTED);
    try {
        // 若 request 已取消,直接 finish 它
        if (request.isCanceled()) {
            request.finish("cache-discard-canceled");
            return;
        }
        // 通过 request 尝试获取 Entry
        Cache.Entry entry = mCache.get(request.getCacheKey());
        if (entry == null) {
        		// 若缓存未命中,则将其放入 mNetworkQueue 等待进行网络请求
            request.addMarker("cache-miss");
            // Cache miss; send off to the network dispatcher.
            if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
                mNetworkQueue.put(request);
            }
            return;
        }
        // 如果缓存过期,放入 mNetworkQueue 等待进行网络请求
        if (entry.isExpired()) {
            request.addMarker("cache-hit-expired");
            request.setCacheEntry(entry);
            if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
                mNetworkQueue.put(request);
            }
            return;
        }
        // 缓存命中,转换并获取 Response
        request.addMarker("cache-hit");
        Response<?> response =
                request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
        request.addMarker("cache-hit-parsed");
        if (!entry.refreshNeeded()) {
            // 如果得到的 response 不需要刷新,直接交付 Response
            mDelivery.postResponse(request, response);
        } else {
            // 如果缓存到期,则将当前 response 设为中间 Response,进行 Response 交付
            // 当 Response 成功交付,则会将其放入 mNetworkQueue 等待网络请求
            request.addMarker("cache-hit-refresh-needed");
            request.setCacheEntry(entry);
            // Mark the response as intermediate.
            response.intermediate = true;
            if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
                // Post the intermediate response back to the user and have
                // the delivery then forward the request along to the network.
                mDelivery.postResponse(
                        request,
                        response,
                        new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    mNetworkQueue.put(request);
                                } catch (InterruptedException e) {
                                    Thread.currentThread().interrupt();
                                }
                            }
                        });
            } else {
                // request has been added to list of waiting requests
                // to receive the network response from the first request once it returns.
                mDelivery.postResponse(request, response);
            }
        }
    } finally {
        request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_FINISHED);
    }
}

代码比较长,它主要有以下的步骤:

  1. Request 已取消,直接 finish 它。
  2. 通过 Request 尝试获取缓存的 Response 对应的 Entry
  3. 若缓存未命中,则将其放入 mNetworkQueue 等待进行网络请求。
  4. 若缓存过期,则将其放入 mNetworkQueue 等待进行网络请求。
  5. 若缓存命中,转换并获取 Response
  6. 若得到的 Response 不需要刷新,直接交付 Response
  7. 若检测 Response 发现缓存到期,则将当前 Response 设为中间 Response,进行 Response 交付。此时传入了一个 Runnable,当 Response 成功交付时,会将 Request放入 mNetworkQueue 等待网络请求。

简单点说就是,如果缓存命中且 Response 未过期,则直接将其交付,否则将其放入 mNetworkQueue 等待网络请求。

Request

前面提到的很多机制都与 Request 有关,为了更好的理解,我们暂时先不关心 Network 是如何发起网络请求的,让我们先看看 Request 的组成。

Request 只是一个抽象类,它的几个实现如下:

  • StringRequest:返回 StringRequest
  • JsonRequestJsonObjectRequestJsonArrayRequest 的基类。
  • JsonObjectRequest:返回 JsonObjectRequest
  • JsonArrayRequest:返回 JsonArrayRequest
  • ImageRequest:返回图片的 Request
  • ClearCacheRequest:一个用来清空缓存的 Request,并不是用来进行网络请求的 Request

构建

要构建一个 Request,我们可以通过其构造函数,其中不同的子类有不同的构造函数,主要的不同是体现在其传入的 Listener 的不同,例如 StringRequest 的构造函数如下:

public StringRequest(int method, String url, Listener<String> listener, @Nullable ErrorListener errorListener) {
    super(method, url, errorListener);
    mListener = listener;
}

它们都会调用 Request(method, url, errorListener) 的构造函数:

public Request(int method, String url, @Nullable Response.ErrorListener listener) {
    mMethod = method;
    mUrl = url;
    mErrorListener = listener;
    setRetryPolicy(new DefaultRetryPolicy());
    mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
}

如果想要创建一个 POST 请求并传入参数,需要重写它的 getParams 方法:

protected Map<String, String> getParams() throws AuthFailureError {
    return null;
}

取消

取消一个 Request 十分简单,只需要将 mCanceled 置为 true 即可:

public void cancel() {
    synchronized (mLock) {
        mCanceled = true;
        mErrorListener = null;
    }
}

Response 转换

RequestparseNetworkResponse 方法是一个抽象方法,需要不同子类去具体实现。

例如 StringRequest 的实现如下:

@Override
@SuppressWarnings("DefaultCharset")
protected Response<String> parseNetworkResponse(NetworkResponse response) {
    String parsed;
    try {
        parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
    } catch (UnsupportedEncodingException e) {
        // Since minSdkVersion = 8, we can't call
        // new String(response.data, Charset.defaultCharset())
        // So suppress the warning instead.
        parsed = new String(response.data);
    }
    return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}

非常简单,根据 Response.data 转化为了对应的 String

Response 的交付

Response 的交付方法 deliverResponse 是一个抽象方法,需要不同子类去具体实现,它主要做的事就是调用 ListeneronResponse 方法,并传入转换后的对象。

例如 StringRequest 的实现如下:

@Override
protected void deliverResponse(String response) {
    Response.Listener<String> listener;
    synchronized (mLock) {
        listener = mListener;
    }
    if (listener != null) {
        listener.onResponse(response);
    }
}

Error 的交付

Error 的交付在 Request 中进行了实现,也非常简单,实际上就是调用 ErrorListeneronErrorResponse 方法:

public void deliverError(VolleyError error) {
    Response.ErrorListener listener;
    synchronized (mLock) {
        listener = mErrorListener;
    }
    if (listener != null) {
        listener.onErrorResponse(error);
    }
}

结束

/**
 * Notifies the request queue that this request has finished (successfully or with error).
 *
 * <p>Also dumps all events from this request's event log; for debugging.
 */
void finish(final String tag) {
    if (mRequestQueue != null) {
        mRequestQueue.finish(this);
    }
    if (MarkerLog.ENABLED) {
        final long threadId = Thread.currentThread().getId();
        if (Looper.myLooper() != Looper.getMainLooper()) {
            // If we finish marking off of the main thread, we need to
            // actually do it on the main thread to ensure correct ordering.
            Handler mainThread = new Handler(Looper.getMainLooper());
            mainThread.post(
                    new Runnable() {
                        @Override
                        public void run() {
                            mEventLog.add(tag, threadId);
                            mEventLog.finish(Request.this.toString());
                        }
                    });
            return;
        }
        mEventLog.add(tag, threadId);
        mEventLog.finish(this.toString());
    }
}

主要是调用了 RequestQueue.finish 方法,它会将当前 Request 从正在执行的 Request 队列移除。

缓存

Response 被缓存之前会调用 Request.shouldCache 判断是否需要缓存,

/** Whether or not responses to this request should be cached. */
// TODO(#190): Turn this off by default for anything other than GET requests.
private boolean mShouldCache = true;

可以看到,除了 GET 请求,其余请求方式都不允许缓存。

我们再看看存在缓存中的 key 是如何通过 Request 转换而得:

public String getCacheKey() {
    String url = getUrl();
    // If this is a GET request, just use the URL as the key.
    // For callers using DEPRECATED_GET_OR_POST, we assume the method is GET, which matches
    // legacy behavior where all methods had the same cache key. We can't determine which method
    // will be used because doing so requires calling getPostBody() which is expensive and may
    // throw AuthFailureError.
    // TODO(#190): Remove support for non-GET methods.
    int method = getMethod();
    if (method == Method.GET || method == Method.DEPRECATED_GET_OR_POST) {
        return url;
    }
    return Integer.toString(method) + '-' + url;
}

由于新版 Volley 中只支持 GET 请求进行缓存,因此 Request 是以 url 作为缓存的 key

Event 机制

我们暂时先不关心 Network 的具体实现,让我们先看看 Event 发出后做了什么。

调用 sendEvent 后,转调到了 RequestQueue.sendRequestEvent 中:

void sendRequestEvent(Request<?> request, @RequestEvent int event) {
    synchronized (mEventListeners) {
        for (RequestEventListener listener : mEventListeners) {
            listener.onRequestEvent(request, event);
        }
    }
}

可以看出,我们的用户可以向 RequestQueue 中注册一个 RequestEventListener 来监听 Request 相关的 Event

Response

相比 RequestResponse 就简单了很多(感觉很多 Response 的功能放在了 Request),里面主要是携带了一些请求结束后的相关信息。

public class Response<T> {

    /** Callback interface for delivering parsed responses. */
    public interface Listener<T> {
        /** Called when a response is received. */
        void onResponse(T response);
    }

    /** Callback interface for delivering error responses. */
    public interface ErrorListener {
        /**
         * Callback method that an error has been occurred with the provided error code and optional
         * user-readable message.
         */
        void onErrorResponse(VolleyError error);
    }

    /** Returns a successful response containing the parsed result. */
    public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
        return new Response<>(result, cacheEntry);
    }

    /**
     * Returns a failed response containing the given error code and an optional localized message
     * displayed to the user.
     */
    public static <T> Response<T> error(VolleyError error) {
        return new Response<>(error);
    }

    /** Parsed response, or null in the case of error. */
    public final T result;

    /** Cache metadata for this response, or null in the case of error. */
    public final Cache.Entry cacheEntry;

    /** Detailed error information if <code>errorCode != OK</code>. */
    public final VolleyError error;

    /** True if this response was a soft-expired one and a second one MAY be coming. */
    public boolean intermediate = false;

    /** Returns whether this response is considered successful. */
    public boolean isSuccess() {
        return error == null;
    }

    private Response(T result, Cache.Entry cacheEntry) {
        this.result = result;
        this.cacheEntry = cacheEntry;
        this.error = null;
    }

    private Response(VolleyError error) {
        this.result = null;
        this.cacheEntry = null;
        this.error = error;
    }
}

Network

从前面的分析中,我们可以知道,真正的网络请求是通过 Network 类实现的,它是一个抽象类,只有一个子类 BaseNetwork

我们主要关注它的 performRequest 方法:

@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 {
            // 获取 Header
            Map<String, String> additionalRequestHeaders =
                    getCacheHeaders(request.getCacheEntry());
						// 通过 httpStack.executeRequest 来同步执行网络请求
            httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);
            int statusCode = httpResponse.getStatusCode();
            responseHeaders = httpResponse.getHeaders();
            // 若没有 Modified,则构建一个 NetworkResponse 并返回
            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);
                }
                // 将 response 的 header 和缓存的 entry 结合,变成新的 Headers
                List<Header> combinedHeaders = combineHeaders(responseHeaders, entry);
                return new NetworkResponse(
                        HttpURLConnection.HTTP_NOT_MODIFIED,
                        entry.data,
                        /* notModified= */ true,
                        SystemClock.elapsedRealtime() - requestStart,
                        combinedHeaders);
            }
            // 有的 Response 没有 content,因此需要对 content 进行判断
            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();
            }
            // 返回请求结果构建的 Response
            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());
            }
        }
    }
}

这里的代码非常长,主要是下面的步骤:

  1. 不断循环进行请求,直到出现错误或请求成功
  2. 获取 Headers
  3. 之后通过 mBaseHttpStack 进行网络请求
  4. 如果请求结果没有 Modified,则将返回的 Headers 与缓存的信息结合构建 NetworkResponse 并返回。
  5. 最后如果请求成功,构建 NetworkResponse 并返回。
  6. 若出现错误,如果是可以进行重试的(如 3xx 状态码或超时),会尝试进行重试。
  7. 否则会根据具体错误构建对应的 ErrorResponse 并返回。

HttpStack

HttpStack 是我们真正执行网络请求的类,它有两个实现:HurlStack 以及 HttpClientStack,前者基于 HttpUrlConnection 实现,后者基于 HttpClient 实现。

由于 HttpClient 已经被彻底抛弃,并且目前几乎已经不存在 SDK 9 以下的机器,因此我们只需要分析 HurlStack 即可,我们看到其 executeRequest 方法:

@Override
public HttpResponse executeRequest(Request<?> request, Map<String, String> additionalHeaders)
        throws IOException, AuthFailureError {
    String url = request.getUrl();
    HashMap<String, String> map = new HashMap<>();
    map.putAll(additionalHeaders);
    // Request.getHeaders() takes precedence over the given additional (cache) headers).
    map.putAll(request.getHeaders());
    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);
    boolean keepConnectionOpen = false;
    try {
        for (String headerName : map.keySet()) {
            connection.setRequestProperty(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.");
        }
        if (!hasResponseBody(request.getMethod(), responseCode)) {
            return new HttpResponse(responseCode, convertHeaders(connection.getHeaderFields()));
        }
        // Need to keep the connection open until the stream is consumed by the caller. Wrap the
        // stream such that close() will disconnect the connection.
        keepConnectionOpen = true;
        return new HttpResponse(
                responseCode,
                convertHeaders(connection.getHeaderFields()),
                connection.getContentLength(),
                new UrlConnectionInputStream(connection));
    } finally {
        if (!keepConnectionOpen) {
            connection.disconnect();
        }
    }
}

这里没什么难度,就不再详细介绍,主要是调用了 HttpUrlConnection 的 API 进行网络请求。

缓存机制

Volley 的缓存机制基于 Cache 这个接口实现,它对外暴露了 putget 等常见的缓存操作接口。默认情况下采用基于磁盘的缓存 DiskBasedCache

这一块主要是一些对文件的读取与写入,暂时就不研究了,有兴趣的读者可以自行阅读。

总结

为了在 Application 调用 newRequestQueue 同时又不被 StrictMode 有关文件操作相关的规则所影响,Volley 中使用了一个 FileSupplier 来对 File 进行包装,它采用了一个懒创建的思路,只有用到的时候才创建对应的 cacheDir 文件

RequestQuque 维护了三个队列,分别是待请求缓存队列待网络请求队列以及正在请求队列

RequestQueue 默认情况下维护了 4 个 NetworkDispatcher 以及 1 个 CacheDispatcher,它们都是继承自 Thread,通过异步进行网络请求的方式从而提高请求效率。

请求的成功或失败都会通过 ExecutorDelivery 进行交付,然后通过 Request 通知到各个 Listener

存在一套缓存机制,以 Request 为 Key,以 Response 为 Value。只有 GET 请求需要进行缓存,所有 GET 请求会首先被放入缓存队列 mCacheQueue,当缓存未命中或缓存过期时,才会被放入 mNetQueue 进行网络请求

Network 是一个对网络请求的执行进行包装的类,它主要负责了 Response 的转换以及对重试的支持。

HttpStack 是真正执行网络请求的类,它在高于 SDK 9 的版本下会基于 HttpUrlConnection 进行网络请求,否则会基于 HttpClient 进行网络请求。

Cache 实现了 Volley 的缓存机制,默认情况下采用基于磁盘的缓存 DiskBasedCache

Volley 虽没有像 OkHttp 那样灵活的拦截器机制,但提供了很多 Listener 供外界对请求过程进行监听。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章