Okhttp 请求流程梳理

最近在看 Okhttp 的源码。不得不说源码设计的很巧妙,从中能学到不少。其实网上关于 Okhttp 的文章已经不少了,本身也看了不少。可是俗话说得好,好记性不如烂笔头,当你动手的时候,你会发现你在看的时候没有注意到的不少细节。缓存

本次要分析的 Okhttp 版本是 3.8.1,在 gradle 中引用以下:安全

implementation 'com.squareup.okhttp3:okhttp:3.8.1'
implementation 'com.squareup.okio:okio:1.7.0'

之因此选择分析3.8.1,是由于最新版是采用 Kotlin 写的,由于本人 Kotlin 实力不容许,因此只能分析 Java 版本。服务器

使用示例

一、发起一个异步 GET 请求,代码具体以下:cookie

String url = "http://wwww.baidu.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
        .url(url)
        .get()//默认就是GET请求,能够不写
        .build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        Log.d(TAG, "onFailure: ");
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        Log.d(TAG, "onResponse: " + response.body().string());
    }
});

 二、发起一个同步 GET 请求,代码具体以下:网络

String url = "http://wwww.baidu.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
        .url(url)
        .build();
final Call call = okHttpClient.newCall(request);
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            Response response = call.execute();
            Log.d(TAG, "run: " + response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}).start();

 能够看到两个请求基本大同小异,总结下过程以下:app

  • 先建立 OkHttpClient 实例;less

  • 构造 Request 实例,传入 url 等相关参数;dom

  • 经过前两步中的实例对象构建 Call 对象;异步

  • 异步请求经过 Call#enqueue(Callback) 方法来提交异步请求,同步请求经过 Call#execute() 直接获取 Reponse ;socket

经过示例,你们简单了解 Okhttp 中的一些对象,下面开始梳理整个请求逻辑。先从 OkHttpClient 开始。

OkHttpClient

当咱们发起请求的时候,须要先构造 okHttpClient 对象,代码具体以下:

  public OkHttpClient() {
    this(new Builder());
  }

能够发现是使用了 builder 建造者模式;来看看里面的内容

public Builder() {
      dispatcher = new Dispatcher();  // 调度器
      protocols = DEFAULT_PROTOCOLS;  // 协议
      connectionSpecs = DEFAULT_CONNECTION_SPECS; //传输层版本和链接协议
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;    //cookie
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;   //证书链
      proxyAuthenticator = Authenticator.NONE;    //代理身份验证
      authenticator = Authenticator.NONE;    //本地身份验证
      connectionPool = new ConnectionPool();    //连接池 复用链接
      dns = Dns.SYSTEM;   //域名
      followSslRedirects = true;
      followRedirects = true;   //本地重定向
      retryOnConnectionFailure = true;
      connectTimeout = 10_000;
      readTimeout = 10_000;   //读取超时
      writeTimeout = 10_000;  //写入超时
      pingInterval = 0;
}
OkHttpClient 内部已经实现了 OkHttpClient(Builder builder),若是咱们不须要配置 client,okhttp 已将帮咱们默认实现了配置。总结起来主要有如下几点:
  • 里面包含了不少对象,其实 OKhttp 的不少功能模块都包装进这个类,让这个类单独提供对外的 API,这种外观模式的设计十分的优雅,叫作外观模式。
  • 而内部模块比较多,就使用了 Builder 模式(建造器模式),一般用于参数比较多状况。
  • 它的方法只有一个:newCall 返回一个 Call 对象(一个准备好了的能够执行和取消的请求)。

Request

接下来,咱们看 Request 请求类,主要包含下面几个属性: url,请求方法名,请求头部,请求体,从属性就能够判断出 Request 主要做用。
  Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tag = builder.tag != null ? builder.tag : this;
  }

 Call

HTTP请求任务封装。能够说咱们能用到的操纵基本上都定义在这个接口里面了,因此也能够说这个类是 Okhttp 类的核心类了。咱们能够经过 Call 对象来操做请求了。而 Call 接口内部提供了 Factory 工厂方法模式 (将对象的建立延迟到工厂类的子类去进行,从而实现动态配置),下面是 Call 接口的具体内容:

public interface Call extends Cloneable {
  /** Returns the original request that initiated this call. */
  Request request();

  /**
   * Invokes the request immediately, and blocks until the response can be processed or is in
   * error.
   *
   * <p>To avoid leaking resources callers should close the {@link Response} which in turn will
   * close the underlying {@link ResponseBody}.
   *
   * <pre>@{code
   *
   *   // ensure the response (and underlying response body) is closed
   *   try (Response response = client.newCall(request).execute()) {
   *     ...
   *   }
   *
   * }</pre>
   *
   * <p>The caller may read the response body with the response's {@link Response#body} method. To
   * avoid leaking resources callers must {@linkplain ResponseBody close the response body} or the
   * Response.
   *
   * <p>Note that transport-layer success (receiving a HTTP response code, headers and body) does
   * not necessarily indicate application-layer success: {@code response} may still indicate an
   * unhappy HTTP response code like 404 or 500.
   *
   * @throws IOException if the request could not be executed due to cancellation, a connectivity
   * problem or timeout. Because networks can fail during an exchange, it is possible that the
   * remote server accepted the request before the failure.
   * @throws IllegalStateException when the call has already been executed.
   */
  Response execute() throws IOException;

  /**
   * Schedules the request to be executed at some point in the future.
   *
   * <p>The {@link OkHttpClient#dispatcher dispatcher} defines when the request will run: usually
   * immediately unless there are several other requests currently being executed.
   *
   * <p>This client will later call back {@code responseCallback} with either an HTTP response or a
   * failure exception.
   *
   * @throws IllegalStateException when the call has already been executed.
   */
  void enqueue(Callback responseCallback);

  /** Cancels the request, if possible. Requests that are already complete cannot be canceled. */
  void cancel();

  /**
   * Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain
   * #enqueue(Callback) enqueued}. It is an error to execute a call more than once.
   */
  boolean isExecuted();

  boolean isCanceled();

  /**
   * Create a new, identical call to this one which can be enqueued or executed even if this call
   * has already been.
   */
  Call clone();

  interface Factory {
    Call newCall(Request request);
  }
}

 RealCall

 RealCall 继承自 Call,是真正发起请求的的实体类。RealCall 主要方法:

  • 同步请求 :client.newCall(request).execute();

  • 异步请求: client.newCall(request).enqueue();

下面咱们来看看里面具体的内容:

 RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    final EventListener.Factory eventListenerFactory = client.eventListenerFactory();

    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);

    // TODO(jwilson): this is unsafe publication and not threadsafe.
    this.eventListener = eventListenerFactory.create(this);
  }

能够发现,其内部持有了 client,原始请求,以及请求事件回调 Listener 等。咱们看下请求的回调 Listener 的具体内容:

  public void fetchStart(Call call) {
  }

  public void dnsStart(Call call, String domainName) {
  }

  public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList,
      Throwable throwable) {
  }

  public void connectStart(Call call, InetAddress address, int port) {
  }

  public void secureConnectStart(Call call) {
  }

  public void secureConnectEnd(Call call, Handshake handshake,
      Throwable throwable) {
  }

  public void connectEnd(Call call,  InetAddress address, int port, String protocol,
      Throwable throwable) {
  }

  public void requestHeadersStart(Call call) {
  }

  public void requestHeadersEnd(Call call, Throwable throwable) {
  }

  public void requestBodyStart(Call call) {
  }

  public void requestBodyEnd(Call call, Throwable throwable) {
  }

  public void responseHeadersStart(Call call) {
  }

  public void responseHeadersEnd(Call call, Throwable throwable) {
  }

  public void responseBodyStart(Call call) {
  }

  public void responseBodyEnd(Call call, Throwable throwable) {
  }

  public void fetchEnd(Call call, Throwable throwable) {
  }

能够看到 OkHttp 的回调作得很是细致,有各类各样的回调,无论你想不想用,都帮你考虑到了呢。这样咱们能够监听具体的回调,而后作一些操做。

接下去就要开始讲异步请求的具体步骤呢。先从异步请求讲起,这也是咱们最经常使用的。

// RealCall  
@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

能够看到上述代码作了几件事:

  • synchronized (this) 确保每一个call只能被执行一次不能重复执行,若是想要彻底相同的 call,能够调用以下方法:进行克隆
@SuppressWarnings("CloneDoesntCallSuperClone") // We are a final type & this saves clearing state.
@Override public RealCall clone() {
    return RealCall.newRealCall(client, originalRequest, forWebSocket);
}
  • 利用 dispatcher 调度器,来进行实际的执行 client.dispatcher().enqueue(new AsyncCall(responseCallback)),在上面的 OkHttpClient.Builder 能够看出已经初始化了 Dispatcher。

心细的读者可能发现一个问题了,那就是这里 enqueue 明明是一个封装了 responseCallback 的 AsyncCall ,怎么就会变成加入队列执行请求了呢?这个下面我会进行解释。

Dispatcher 

Dispatcher 是 Okhttp 的调度器,用来管理控制请求的队列。内部经过线程池来确保队列的有序运行。先看下 enqueue 方法的具体内容:

 synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

能够看到内部存在两个队列,一个是正在运行的队列 runningAsyncCalls,另外一个是 readyAsyncCalls 队列。若是当前运行数小于最大运行数,而且当前请求的host小于最大请求个数,那么就会直接加入运行队列,并运行。若是超了,就会加入准备队列。

  /** Ready async calls in the order they'll be run. */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

实际上还有一个同步队列,没有给同步队列作限制,只要一加入就开始执行请求。

当请求队列完成请求后须要进行移除,看下 finished 的代码逻辑:

  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

能够看到,这是使用了泛型,不用关心具体传入的队列是哪个,直接就能够移除。promoteCalls 为 true 表明是异步请求队列,还得从 readyAsyncCalls 队列里面取出一个队列添加到 runningAsyncCalls 队列里面去执行请求。

private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

经过上述代码,关于调度器的功能做用就基本理清了。

AsyncCall

 AsyncCall 是 RealCall 里面的内部类,继承自 NamedRunnable,是自定义的Runnable,能够为线程设置 name。内部代码具体以下:

public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

能够发现,在 run 方法内部调用了execute 方法,这个方法就是真正的发起请求的逻辑。下面咱们看下 AsyncCall 中的该方法得具体内容:

  @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

获取响应数据最终是是经过 getResponseWithInterceptorChain() 来获取的。而后经过回调将 Response 返回给用户。

值得注意的 finally 执行了client.dispatcher().finished(this) 经过调度器移除队列。移除得逻辑在前面也已经讲过了。

下面看下 getResponseWithInterceptorChain 方法内部的具体逻辑:

//Realcall 核心代码 开始真正的执行网络请求
  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    // 责任链
    List<Interceptor> interceptors = new ArrayList<>();
    // 在配置okhttpClient 时设置的intercept 由用户本身设置
    interceptors.addAll(client.interceptors());
    // 负责处理失败后的重试与重定向
    interceptors.add(retryAndFollowUpInterceptor);
    // 负责把用户构造的请求转换为发送到服务器的请求 、把服务器返回的响应转换为用户友好的响应 处理 配置请求头等信息
    // 从应用程序代码到网络代码的桥梁。首先,它根据用户请求构建网络请求。而后它继续呼叫网络。最后,它根据网络响应构建用户响应。
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    // 处理 缓存配置 根据条件(存在响应缓存并被设置为不变的或者响应在有效期内)返回缓存响应
    // 设置请求头(If-None-Match、If-Modified-Since等) 服务器可能返回304(未修改)
    // 可配置用户本身设置的缓存拦截器
    interceptors.add(new CacheInterceptor(client.internalCache()));
    // 链接服务器 负责和服务器创建链接 这里才是真正的请求网络
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      // 配置okhttpClient 时设置的networkInterceptors
      // 返回观察单个网络请求和响应的不可变拦截器列表。
      interceptors.addAll(client.networkInterceptors());
    }
    // 执行流操做(写出请求体、得到响应数据) 负责向服务器发送请求数据、从服务器读取响应数据
    // 进行http请求报文的封装与请求报文的解析
    interceptors.add(new CallServerInterceptor(forWebSocket));

    // 建立责任链
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    //TODO 执行责任链
    return chain.proceed(originalRequest);
  }
从上述代码中,能够看出都实现了 Interceptor 接口,这是 Okhttp 最核心的部分,采用责任链的模式来使每一个功能分开,每一个 Interceptor 自行完成本身的任务,而且将不属于本身的任务交给下一个,简化了各自的责任和逻辑。

RealInterceptorChain

那责任链是怎么实现的呢?下面具体分析下相关逻辑:
  public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, RealConnection connection, int index, Request request) {
    this.interceptors = interceptors;
    this.connection = connection;
    this.streamAllocation = streamAllocation;
    this.httpCodec = httpCodec;
    this.index = index;
    this.request = request;
  }
// 责任链处理
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // If we already have a stream, confirm that the incoming request will use it.
    if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // If we already have a stream, confirm that this is the only call to chain.proceed().
    if (this.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

    // Confirm that the intercepted response isn't null.
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }

    return response;
  }

 首先是看构造函数,内部持有了当前责任链的全部拦截器list,还包括 RealConnection,index (当前正在处理拦截器索引)等。

接下去看 proceed 方法里的逻辑,归来起来就是以下:

  1. 首先是经过 index 和 calls 来作了一些安全判断,避免重复处理,。

  2. 将索引号 index +1,新建立一个 chain。

  3. 根据目前的 index 获取拦截器,而后将新的 chain 传入到获取拦截器中。

  4. 拦截器作完本身的操做后,会调用新建立的 chain 的 proceed 方法,交由下一个拦截器来处理。

  5. 当数据返回后,从后往前,拦截器会依次对数据作一些处理,最终用户得到请求的数据。

经过上述往复循环,最终全部的拦截器都会走两遍,一次是对请求体作操做,一次是对返回体作操做,最终用户得到处理后的数据。

下面来看一个具体的拦截器 。

 CacheInterceptor

CacheInterceptor 代码比较长,咱们一步一步的来进行分析。

首先咱们先分析上部分代码当没有网络的状况下是如何处理获取缓存的。

  @Override public Response intercept(Chain chain) throws IOException
  {
// 获取request对应缓存的Response 若是用户没有配置缓存拦截器 cacheCandidate == null
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;

    // 执行响应缓存策略
    long now = System.currentTimeMillis();
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    // 若是networkRequest == null 则说明不使用网络请求
    Request networkRequest = strategy.networkRequest;
    // 获取缓存中(CacheStrategy)的Response
    Response cacheResponse = strategy.cacheResponse;

    if (cache != null) {
      cache.trackResponse(strategy);
    }
    // 缓存无效 关闭资源
    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    }

    // If we're forbidden from using the network and the cache is insufficient, fail.
    // networkRequest == null 不实用网路请求 且没有缓存 cacheResponse == null  返回失败
    if (networkRequest == null && cacheResponse == null) {
      return new Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(Util.EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
    }

    // 不使用网络请求 且存在缓存 直接返回响应
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }
    }

上述的代码,主要作了几件事:

  1. 若是用户本身配置了缓存拦截器,cacheCandidate = cache.Response 获取用户本身存储的 Response,不然 cacheCandidate = null,同时从 CacheStrategy 获取cacheResponse 和 networkRequest;

  2. 若是 cacheCandidate != null 而 cacheResponse == null 说明缓存无效清楚 cacheCandidate 缓存。

  3. 若是 networkRequest == null 说明没有网络,cacheResponse == null 没有缓存,返回失败的信息,责任链此时也就终止,不会在往下继续执行。

  4. 若是 networkRequest == null 说明没有网络,cacheResponse != null 有缓存,返回缓存的信息,责任链此时也就终止,不会在往下继续执行。

上部分代码,其实就是没有网络的时候的处理。那么下部分代码确定是,有网络的时候处理:

    // 执行下一个拦截器
    Response networkResponse = null;
    try {
      networkResponse = chain.proceed(networkRequest);
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }

    // 网络请求 回来 更新缓存
    // If we have a cache response too, then we're doing a conditional get.
    // 若是存在缓存 更新
    if (cacheResponse != null) {
      // 304响应码 自从上次请求后,请求须要响应的内容未发生改变
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        Response response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
            .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        networkResponse.body().close();

        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
        cache.trackConditionalCacheHit();
        cache.update(cacheResponse, response);
        return response;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }
    // 缓存Response
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

    if (cache != null) {
      if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
        // Offer this request to the cache.
        CacheRequest cacheRequest = cache.put(response);
        return cacheWritingResponse(cacheRequest, response);
      }

      if (HttpMethod.invalidatesCache(networkRequest.method())) {
        try {
          cache.remove(networkRequest);
        } catch (IOException ignored) {
          // The cache cannot be written.
        }
      }
    }

    return response;
  }

 上面的代码主要作了这几件事:

  1. 执行下一个拦截器,也就是请求网络

  2. 责任链执行完毕后,会返回最终响应数据,若是缓存存在更新缓存,若是缓存不存在加入到缓存中去。

这就跟前面讲的对应上了,请求前作一些处理,好比判断缓存是否存在,网络是否可用等操做;数据回来以后,更新缓存,在传给上一个拦截器去作处理。

这样就体现出了责任链的好处了,当责任链执行完毕,若是拦截器想要拿到最终的数据作其余的逻辑处理等,这样就不用在作其余的调用方法逻辑了,直接在当前的拦截器就能够拿到最终的数据。这也是okhttp设计的最优雅最核心的功能。

到这里,异步请求逻辑基本就梳理完了。

同步请求

同步请求会直接调用 Call#ececute 方法,记住这个 execute 方法的返回实体是 Reponse,因此它直接返回了请求。 

// RealCall
// 同步执行请求 直接返回一个请求的结果
  @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    // 调用监听的开始方法
    eventListener.callStart(this);
    try {
      // 添加到队列中去
      client.dispatcher().executed(this);
      // 获取请求的返回数据
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      // 执行调度器的完成方法 移除队列
      client.dispatcher().finished(this);
    }
  }

主要作了几件事:

  1. synchronized (this) 避免重复执行,上面的文章部分有讲。

  2. client.dispatcher().executed(this),实际上调度器只是将 call 加入到了同步执行队列中。

  3. getResponseWithInterceptorChain() 最核心的代码,至关于同步请求直接就开始运行,请求网络获得响应数据,返回给用户

  4. client.dispatcher().finished(this);  执行调度器的完成方法 移除队列

能够看出,在同步请求的方法中,涉及到 dispatcher 只是告知了执行状态,开始执行了(调用 executed),执行完毕了(调用 finished)其余的并无涉及到。dispatcher 更多的是服务异步请求。

以上就是对 Okhttp 请求流程的梳理,后面附一张盗的流程图

相关文章
相关标签/搜索