OkHttp 源碼分析系列(一)- Okhttp同步請求、異步請求過程

??RxJava源碼的基礎(chǔ)部分分析的差不多,后續(xù)如果有新的內(nèi)容話,會繼續(xù)的補(bǔ)充。從今天開始,我們來看看OkHttp的相關(guān)源碼。OkHttp的源碼過于復(fù)雜,涉及到的方面非常的多,本系列文章目的是打通Okhttp的整個執(zhí)行流程,不對某一個細(xì)節(jié)重點(diǎn)分析。
??本篇文章是本系列文章的第一篇,我們先從最簡單的Okhttp使用入手,進(jìn)而分析Okhttp兩種請求方式的流程。

1. 同步請求

??同步請求的重點(diǎn)在于同步二字,顧名思義,執(zhí)行同步請求,不會單獨(dú)的開一個線程,所以在進(jìn)行網(wǎng)絡(luò)請求時,當(dāng)前線程會阻塞在這里。
??我們來簡單的看看,怎么進(jìn)行一個同步請求:

  private final OkHttpClient mOkHttpClient = new OkHttpClient.Builder().readTimeout(50, TimeUnit.SECONDS).build();

  private void syncRequest() {
    Request request = new Request.Builder().url("http://www.baidu.com").get().build();
    Call call = mOkHttpClient.newCall(request);
    try {
      Response response = call.execute();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

??同步請求的執(zhí)行是調(diào)用了callexecute方法。但是在調(diào)用execute方法之前,還要進(jìn)行一些準(zhǔn)備操作。

(1). OkHttpClient的創(chuàng)建

??OkHttpClient是一個非常基礎(chǔ)的類,在使用OkHttp來進(jìn)行網(wǎng)絡(luò)請求時,我們必須先創(chuàng)建OkHttpClient的對象。在這個類里面,我們會配置很多的參數(shù),比如超時連接時間、攔截器等等。我們來詳細(xì)的看一看:

  private final OkHttpClient mOkHttpClient = new OkHttpClient.Builder()
    .readTimeout(50, TimeUnit.SECONDS)
    .build();

??上面就是一個非常簡單的創(chuàng)建列子。現(xiàn)在我們來從源碼的角度來看看OkHttpClient究竟給我們配置那些參數(shù)。
??我們知道OkHttpClient是通過建造者模式來創(chuàng)建的,我們先來看看OkHttpClient.Builder這個類的構(gòu)造方法:

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

??額,構(gòu)造方法里面初始化我們很多的東西,這里我們需要看兩個東西:

      dispatcher = new Dispatcher();
      connectionPool = new ConnectionPool();

?? Dispatcher是一個分發(fā)器,我們所有的Request請求都是通過Dispatcher分發(fā)的。在后續(xù)的文章,我會詳細(xì)講解這個類。本文就不對它做過多的解釋。
??ConnectionPool是一個連接池,很多的請求連接都由這個類管理,比如有些連接需要重用,都由這個類來管理的。這個類跟Dispatcher類一樣,后續(xù)會詳細(xì)的介紹這個類。
??最后就是調(diào)用Builderbuild方法來真正創(chuàng)建OkHttpClient對象。

(2). Request的創(chuàng)建

??Request的創(chuàng)建方式跟 OkHttpClient都是通過Builder方法來創(chuàng)建的,在創(chuàng)建Request的對象時,我們會初始化很多東西,比如請求方式(get或者post)、請求的URL等等。這里就不詳細(xì)的分析了。

(3).Call的創(chuàng)建

??我們調(diào)用OkHttpClientnewCall方法來創(chuàng)建一個Call對象,我們來看看Call對象的創(chuàng)建過程。

  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

??Call是一個接口,它的唯一實(shí)現(xiàn)類是RealCall類,所以我們可以看到在Call方法里面又調(diào)用了RealCallnewRealCall方法。
??我們來看看在RealCallnewRealCall方法里面進(jìn)行哪些操作:

  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;
  }
  private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

??歸根結(jié)底,還是將我們創(chuàng)建好的OkHttpClient對象和Request對象傳遞到RealCall方法里面了。

(4).調(diào)用Call的execute方法

??同步請求的最后一步操作就是調(diào)用Callexecute方法,我們來看看整個execute方法時怎么執(zhí)行的。

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

??我們來重點(diǎn)分析幾個點(diǎn)。首先:

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }

??從這里看的出來,每一個Call只能被執(zhí)行一次,當(dāng)?shù)诙握{(diào)用時,會拋出IllegalStateException異常。
??然后就是這一對:

      client.dispatcher().executed(this);
      client.dispatcher().finished(this);

??在正式進(jìn)行網(wǎng)絡(luò)請求之前,會調(diào)用Dispatcherexecuted方法來將一個Call對象放在一個隊列;在正式進(jìn)行網(wǎng)絡(luò)請求之后,會調(diào)用Dispatcherfinished方法將這個Call對象從隊列中移除。
??但是,這里,我們沒有看到網(wǎng)絡(luò)請求的步驟啊?究竟是哪一步進(jìn)行了網(wǎng)絡(luò)請求呢?沒錯,就是這一步。

      Response result = getResponseWithInterceptorChain();

??getResponseWithInterceptorChain方法通過調(diào)用攔截器鏈的每一個攔截器的proceed方法,最終返回Response對象,就是我們請求的數(shù)據(jù)。攔截器部分,在本篇文章不進(jìn)行解釋,后續(xù)會單獨(dú)解釋OkHttp的攔截器。
??這就是,整個同步請求的執(zhí)行過程,是不是非常的簡單呢?接下來,我們來看看異步請求的。

2. 異步請求

??異步請求的準(zhǔn)備工作跟同步請求差不多,都是先創(chuàng)建OkHttpClient對象、然后創(chuàng)建Request對象,再創(chuàng)建Call對象,最后調(diào)用相應(yīng)的方法來執(zhí)行執(zhí)行這個請求,異步請求調(diào)用的是enqueue方法。我們來看看enqueue方法:

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

??前面部分,我們可以不看,重點(diǎn)在最后一行。我們先來看看Dispatcherenqueue方法:

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

??首先,先判斷當(dāng)前異步請求的數(shù)目是否超過我們設(shè)置的值,這里默認(rèn)值是64,其次在判斷當(dāng)前的請求的主機(jī)是否超過了5個,都不超過的話,那么就將當(dāng)前的Call添加到異步隊列中,然后將當(dāng)前的Call對象提交到線程池中去執(zhí)行;如果超過的話,那么就將當(dāng)前的Call對象放入等待隊列中去。
??我們知道,在同步請求中,是通過調(diào)用getResponseWithInterceptorChain方法來進(jìn)行網(wǎng)絡(luò)請求的,但是在這個異步請求過程中,我們并沒有發(fā)現(xiàn)getResponseWithInterceptorChain方法的調(diào)用,難道是我們分析錯了嗎?并沒有,我們需要在AsyncCall里面來尋找答案。
??我們先來看看AsyncCall的結(jié)構(gòu):

final class AsyncCall extends NamedRunnable {
}

??AsyncCall是繼承于NamedRunnable的,我們再去看看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();
}

??在NamedRunnable中,我們發(fā)現(xiàn)在run方法中調(diào)用了execute方法,最后我們回到了AsyncCall中來,不過這次,我們只需要關(guān)注execute方法就行了。

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

??還是熟悉的味道,跟同步請求的差不多。但是這里我們需要注意的是,從這里可以看出,CallbackonFailure方法和onResponse方法都是在子線程中執(zhí)行的,并沒有通過Handler將消息傳遞到主線程。這里需要特別注意。

3.總結(jié)

??OkHttp兩種請求方式的執(zhí)行流程是比較簡單,現(xiàn)在對它做一個簡單的總結(jié)。
??1.同步請求和異步請求的在執(zhí)行,都必須做同樣的步驟來準(zhǔn)備。包括OkHttpClient的創(chuàng)建,Request的創(chuàng)建,Call的創(chuàng)建。
??2.同步請求是調(diào)用execute方法來執(zhí)行,在execute方法中,通過調(diào)用getResponseWithInterceptorChain方法來真正進(jìn)行網(wǎng)絡(luò)請求,同時每一個Call只能執(zhí)行一次。
??3.異步請求是通過調(diào)用enqueue方法,最后提交到一個線程里面執(zhí)行的。這里需要的注意的是CallbackonFailure方法和onResponse方法都是在子線程執(zhí)行的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容