OkHttp源碼解析之整體流程源碼解析

  • Okhttp在代碼中的構(gòu)建方式
OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS)
                .retryOnConnectionFailure(true)
                .addNetworkInterceptor(getCacheInterceptor()).cache(cache)//設(shè)置緩存
                .addInterceptor(getHttpLoggingInterceptor())//打印日志
                .build();

Request request = new Request.Builder().url("xxxx").build;
Call call = client.newCall(request);
//同步請(qǐng)求
Response response = call.execute();
//異步請(qǐng)求
call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {  
           
            }
        });

同步和異步請(qǐng)求在使用OkHttp時(shí),構(gòu)建client、request、call的過(guò)程是一樣的,只在最后執(zhí)行請(qǐng)求時(shí)存在區(qū)別,內(nèi)部的實(shí)現(xiàn)也存在區(qū)別。

  • 根據(jù)構(gòu)建方式,我們先來(lái)看一下Okhttp的源碼
    (1)先看一下OkHttpClient的內(nèi)部類Builder部分代碼,主要是成員變量
public static final class Builder {
    Dispatcher dispatcher;//重中之重,分發(fā)器,異步請(qǐng)求的分發(fā),就緒隊(duì)列還是執(zhí)行隊(duì)列
    @Nullable Proxy proxy;
    List<Protocol> protocols;
    List<ConnectionSpec> connectionSpecs;
    final List<Interceptor> interceptors = new ArrayList<>();
    final List<Interceptor> networkInterceptors = new ArrayList<>();
    EventListener.Factory eventListenerFactory;
    ProxySelector proxySelector;
    CookieJar cookieJar;
    @Nullable Cache cache;
    @Nullable InternalCache internalCache;
    SocketFactory socketFactory;
    @Nullable SSLSocketFactory sslSocketFactory;
    @Nullable CertificateChainCleaner certificateChainCleaner;
    HostnameVerifier hostnameVerifier;
    CertificatePinner certificatePinner;
    Authenticator proxyAuthenticator;
    Authenticator authenticator;
    ConnectionPool connectionPool;//連接池,非常重要
    Dns dns;
    boolean followSslRedirects;
    boolean followRedirects;
    boolean retryOnConnectionFailure;
    int connectTimeout;
    int readTimeout;
    int writeTimeout;
    int pingInterval;

    public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;
      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;
    }

    Builder(OkHttpClient okHttpClient) {
      this.dispatcher = okHttpClient.dispatcher;
      this.proxy = okHttpClient.proxy;
      this.protocols = okHttpClient.protocols;
      this.connectionSpecs = okHttpClient.connectionSpecs;
      this.interceptors.addAll(okHttpClient.interceptors);
      this.networkInterceptors.addAll(okHttpClient.networkInterceptors);
      this.eventListenerFactory = okHttpClient.eventListenerFactory;
      this.proxySelector = okHttpClient.proxySelector;
      this.cookieJar = okHttpClient.cookieJar;
      this.internalCache = okHttpClient.internalCache;
      this.cache = okHttpClient.cache;
      this.socketFactory = okHttpClient.socketFactory;
      this.sslSocketFactory = okHttpClient.sslSocketFactory;
      this.certificateChainCleaner = okHttpClient.certificateChainCleaner;
      this.hostnameVerifier = okHttpClient.hostnameVerifier;
      this.certificatePinner = okHttpClient.certificatePinner;
      this.proxyAuthenticator = okHttpClient.proxyAuthenticator;
      this.authenticator = okHttpClient.authenticator;
      this.connectionPool = okHttpClient.connectionPool;
      this.dns = okHttpClient.dns;
      this.followSslRedirects = okHttpClient.followSslRedirects;
      this.followRedirects = okHttpClient.followRedirects;
      this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure;
      this.connectTimeout = okHttpClient.connectTimeout;
      this.readTimeout = okHttpClient.readTimeout;
      this.writeTimeout = okHttpClient.writeTimeout;
      this.pingInterval = okHttpClient.pingInterval;
    }
}

Dispatcher dispatcher;//重中之重,分發(fā)器,異步請(qǐng)求的分發(fā),就緒隊(duì)列還是執(zhí)行隊(duì)列。同步請(qǐng)求,簡(jiǎn)單放入同步請(qǐng)求隊(duì)列執(zhí)行。
ConnectionPool connectionPool;//連接池,非常重要,客戶端和服務(wù)器端每一個(gè)連接是一個(gè)connection,而每一個(gè)connection放入連接池中進(jìn)行統(tǒng)一管理。(1)當(dāng)請(qǐng)求的url是同一個(gè)時(shí),可以復(fù)用。(2)ConnectionPool還可以管理連接的策略(打開關(guān)閉,復(fù)用等)

(2)Request同樣也是Builder構(gòu)建模式創(chuàng)建的,其中包含了請(qǐng)求的url,請(qǐng)求頭,網(wǎng)絡(luò)請(qǐng)求方法,請(qǐng)求體等等內(nèi)容。
(3)Call call = client.newCall(request)生成Call請(qǐng)求對(duì)象。

/**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

內(nèi)部實(shí)際是接口Call的實(shí)現(xiàn)類ReallCall執(zhí)行的newRealCall方法。那么我們來(lái)看ReallCall.newReallCall做了什么。

static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }

我們首先看下在創(chuàng)建RealCall的構(gòu)造方法中做了哪些工作。

private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

可以看到RealCall持有了前兩步創(chuàng)建的OkHttpClient對(duì)象和Request對(duì)象,除此之外還創(chuàng)建了一個(gè)重定向攔截器RetryAndFollowUpInterceptor,后續(xù)我們會(huì)介紹。

上面三個(gè)步驟,無(wú)論是同步還是異步都是一樣的,那么他們之間的區(qū)別就是在下面第四步

(4)同步請(qǐng)求

Response response = call.execute();

我們來(lái)看源碼call.execute()具體怎么執(zhí)行

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();//捕捉異常堆棧信息,和主流程無(wú)關(guān),看源碼這種過(guò)濾掉即可。
    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);
    }
  }

首先在同步代碼塊中,判斷executed,如果為true則拋出異常,否則設(shè)置為true,這說(shuō)明同一個(gè)http請(qǐng)求只能請(qǐng)求一次。

client.dispatcher().executed(this);我們主要來(lái)看這一句代碼,dispatcher()方法返回了一個(gè)Dispatcher對(duì)象,具體來(lái)看Dispatcher的executed方法。

synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

可以看到,直接將call添加到同步請(qǐng)求執(zhí)行隊(duì)列當(dāng)中。

返回execute主流程中繼續(xù)看源碼:
Response result = getResponseWithInterceptorChain();

通過(guò)處理攔截器鏈后獲取到服務(wù)器的響應(yīng)結(jié)果,具體后續(xù)詳細(xì)介紹。

最后一步注意finally中的代碼client.dispatcher().finished(this);

最終還是通過(guò)client的分發(fā)器結(jié)束移出請(qǐng)求call對(duì)象,追蹤代碼

/** Used by {@code Call#execute} to signal completion. */
  void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
  }

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

我們可以看到將當(dāng)前Call對(duì)象從同步請(qǐng)求執(zhí)行隊(duì)列中移除,并且通過(guò)runningCallsCount()方法重新計(jì)算了當(dāng)前正在運(yùn)行的異步和同步請(qǐng)求的和。同時(shí)我們可以注意到一個(gè)bool參數(shù)promoteCalls在同步請(qǐng)求中是false,導(dǎo)致promoteCalls()方法不會(huì)執(zhí)行,但是在異步請(qǐng)求中是會(huì)執(zhí)行的,這也就是同步和異步之間的一個(gè)區(qū)別。

總結(jié)一下分發(fā)器在同步請(qǐng)求中的作用:將同步請(qǐng)求添加和移除同步請(qǐng)求
隊(duì)列

異步請(qǐng)求前三步和上邊是一樣的,只有最后一步發(fā)起請(qǐng)求不同,追蹤源碼:

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

異步請(qǐng)求方法enqueue中前幾步依然和同步方法的execute方法相同,不同的在最后一步client.dispatcher().enqueue(new AsyncCall(responseCallback));

那么我們來(lái)跟進(jìn)代碼看一下:

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

在Dispatcher類中定義了兩個(gè)值,一個(gè)maxRequests為64 ,一個(gè)maxRequestsPerHost為5,當(dāng)前運(yùn)行的異步請(qǐng)求不超過(guò)64個(gè)且每個(gè)主機(jī)的請(qǐng)求不超過(guò)5個(gè)則可以將該異步請(qǐng)求添加到正在執(zhí)行的異步請(qǐng)求隊(duì)列中,并通過(guò)一個(gè)線程池去執(zhí)行該Runnable。否則,就將當(dāng)前請(qǐng)求添加到等待的異步請(qǐng)求隊(duì)列中。

接下來(lái)我們來(lái)分析一下線程池執(zhí)行該Runnable的過(guò)程,首先來(lái)看下該線程池。

public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

同步方法構(gòu)造單例對(duì)象executorService,然后調(diào)用了線程池的execute來(lái)運(yùn)行Runnable,那實(shí)際就是執(zhí)行Runnable的run方法,跟蹤代碼:

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

    String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }

    RealCall get() {
      return RealCall.this;
    }

    @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 {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

進(jìn)來(lái)之后并沒有發(fā)現(xiàn)run方法,那么肯定在父類中,我們來(lái)繼續(xù)看NamedRunnable

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

這是一個(gè)抽象類,run中的關(guān)鍵方法execute調(diào)用了一個(gè)抽象方法,那么實(shí)現(xiàn)還是在子類中,我們返回到AsyncCall 中看execute方法。

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 {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

這個(gè)方法在上面已經(jīng)貼過(guò)了,為了便于分析代碼跟蹤流程和實(shí)際代碼,這里再貼一遍。

注意:execute方法執(zhí)行位置是在run方法中,已經(jīng)在子線程中了。getResponseWithInterceptorChain這個(gè)有關(guān)攔截器鏈的方法我們同樣留到后續(xù)講解。
responseCallback.onResponse(RealCall.this, response);成功回調(diào)執(zhí)行的方法。

最后finally同樣執(zhí)行了 client.dispatcher().finished(this);是否似曾相識(shí),沒錯(cuò),同步方法時(shí)也是執(zhí)行了這個(gè)方法,真的是嗎?其實(shí)不是,只是同步方法的一個(gè)重載方法,兩者參數(shù)一個(gè)是RealCall(同步),一個(gè)是AsyncCall(異步,這是個(gè)Runnable)。跟蹤代碼:

/** Used by {@code AsyncCall#run} to signal completion. */
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

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

同步方法時(shí)promoteCalls為false,而異步方法為true,其他部分和同步方法執(zhí)行一致,那么我們單獨(dú)來(lái)看if (promoteCalls) promoteCalls();promoteCalls()這個(gè)方法調(diào)整了我們異步請(qǐng)求的隊(duì)列。

后續(xù)會(huì)有OkHttp中非常重要的調(diào)度器Dispatcher攔截器的介紹。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容