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

你好,我是 N0tExpectErr0r,一名热爱技术的 Android 开发html

个人我的博客:blog.N0tExpectErr0r.cnandroid

本篇源码解析基于 Volley 1.1.1git

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

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) 方法:bash

/**
 * 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 对象。app

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 供外界对请求过程进行监听。

相关文章
相关标签/搜索