1. 程式人生 > >okhttp原始碼分析(一)——基本流程(超詳細)

okhttp原始碼分析(一)——基本流程(超詳細)

1.okhttp原始碼分析(一)——基本流程(超詳細)
2.okhttp原始碼分析(二)——RetryAndFollowUpInterceptor過濾器
3.okhttp原始碼分析(三)——CacheInterceptor過濾器
4.okhttp原始碼分析(四)——ConnectInterceptor過濾器
5.okhttp原始碼分析(五)——CallServerInterceptor過濾器

前言

最近算是入了原始碼的坑了,什麼東西總想按住ctrl看看原始碼的模樣,這段時間在研究okhttp的原始碼,發現okhttp的原始碼完全不是簡簡單單的幾天就可以啃下來的,那就一步一步來吧。
這篇部落格主要是從okhttp的總體流程分析原始碼的執行過程,對okhttp原始碼有大體上的理解,從全域性上看出okhttp的設計思想。

分析

1.OkHttpClient

既然是流程分析,使用過okhttp的都瞭解,首先需要初始化一個OkHttpClient物件。OkHttp支援兩種構造方式
1.預設方式

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

可以看到這種方式,不需要配置任何引數,也就是說基本引數都是預設的,呼叫的是下面的建構函式。

OkHttpClient(Builder builder) {...}

2.builder模式,通過Builder配置引數,最後通過builder()方法返回一個OkHttpClient例項。

public
OkHttpClient build() { return new OkHttpClient(this); }

OkHttpClient基本上就這樣分析完了,裡面的細節基本上就是用於初始化引數和設定引數的方法。所以也必要將大量的程式碼放上來佔內容。。。,這裡另外提一點,從OkHttpClient中可以看出什麼設計模式哪
1.builder模式
2.外觀模式

2.Request

構建完OkHttpClient後就需要構建一個Request物件,檢視Request的原始碼你會發現,你找不多public的建構函式,唯一的一個建構函式是這樣的。

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

這意味著什麼,當然我們構建一個request需要用builder模式進行構建,那麼就看一下builder的原始碼。

public Builder newBuilder() {
    return new Builder(this);
  }
//builder===================
public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }

Builder(Request request) {
      this.url = request.url;
      this.method = request.method;
      this.body = request.body;
      this.tag = request.tag;
      this.headers = request.headers.newBuilder();
    }
public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }

其實忽略其他的原始碼,既然這篇部落格只是為了從總體流程上分析OkHttp的原始碼,所以我們主要著重流程原始碼上的分析。從上面的原始碼我們會發現,request的構建也是基於builder模式。

3.非同步請求

這裡注意一下,這裡分析區分一下同步請求和非同步請求,但其實實質的執行流程除了非同步外,基本都是一致的。
構建完Request後,我們就需要構建一個Call,一般都是這樣的Call call = mOkHttpClient.newCall(request);那麼我們就返回OkHttpClient的原始碼看看。

/**
   * 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 */);
  }

可以看到,這裡實質上呼叫的是RealCall中的newRealCall方法,但是這裡需要注意一點,那就是方法前面的@Override註解,看到這個註解我們就要意識到,這個方法不是繼承就是實現介面。

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {...}

可以看到OkhttpClient實現了Call.Factory介面。

//Call.java
interface Factory {
    Call newCall(Request request);
  }

從介面原始碼我們也可以看出,這個介面其實並不複雜,僅僅是定義一個newCall用於建立Call的方法,這裡其實用到了工廠模式的思想,將構建的細節交給具體實現,頂層只需要拿到Call物件即可。
回到主流程,我們繼續看RealCall中的newRealCall方法。

final class RealCall implements Call {
...
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;
  }
...
}

可以看到RealCall實現了Call介面,newRealCall這是一個靜態方法,new了一個RealCall物件,並建立了一個eventListener物件,從名字也可以看出,這個是用來監聽事件流程,並且從構建方法我們也可以看出,使用了工廠模式

private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    //預設建立一個retryAndFollowUpInterceptor過濾器
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

重點來了,可以看到,在RealCall的建構函式中,除了基本的賦值外,預設建立一個retryAndFollowUpInterceptor過濾器,過濾器可以說是OkHttp的巨大亮點,後續的文章我會詳細分析一些過濾器吧(能力有限,儘量全看看)。

現在Call建立完了,一般就到最後一個步驟了,將請求加入排程,一般的程式碼是這樣的。

//請求加入排程
call.enqueue(new Callback()
        {
            @Override
            public void onFailure(Request request, IOException e)
            {
            }

            @Override
            public void onResponse(final Response response) throws IOException
            {
                    //String htmlStr =  response.body().string();
            }
        });  

可以看到這裡呼叫了call的enqueue方法,既然這裡的call->RealCall,所以我們看一下RealCall的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));
  }

1.首先利用synchronized加入了物件鎖,防止多執行緒同時呼叫,這裡先判斷一下executed是否為true判斷當前call是否被執行了,如果為ture,則丟擲異常,沒有則設定為true。
2.captureCallStackTrace()

private void captureCallStackTrace() {
    Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
    retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
  }

可以看到這裡大體上可以理解為為retryAndFollowUpInterceptor加入了一個用於追蹤堆疊資訊的callStackTrace,後面有時間再詳細分析一下這部分吧,不影響總體流程理解。
3.eventListener.callStart(this);可以看到前面構建的eventListener起到作用了,這裡先回調callStart方法。
4.client.dispatcher().enqueue(new AsyncCall(responseCallback));這裡我們就需要先回到OkHttpClient的原始碼中。

public Dispatcher dispatcher() {
    return dispatcher;
  }

可以看出返回了一個僅僅是返回了一個DisPatcher物件,那麼就追到Dispatcher原始碼中。

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

這裡先對Dispatcher的成員變數做個初步的認識。

public final class Dispatcher {
  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;
  private @Nullable Runnable idleCallback;

  /** Executes calls. Created lazily. */
  private @Nullable ExecutorService executorService;

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

可以看到,這裡用三個佇列ArrayDeque用於儲存Call物件,分為三種狀態非同步等待,同步running,非同步running
所以這裡的邏輯就比較清楚了。

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

當正在執行的非同步佇列個數小於maxRequest(64)並且請求同一個主機的個數小於maxRequestsPerHost(5)時,則將這個請求加入非同步執行佇列runningAsyncCall,並用執行緒池執行這個call,否則加入非同步等待佇列。這裡可以看一下runningCallsForHost方法。

/** Returns the number of running calls that share a host with {@code call}. */
  private int runningCallsForHost(AsyncCall call) {
    int result = 0;
    for (AsyncCall c : runningAsyncCalls) {
      if (c.host().equals(call.host())) result++;
    }
    return result;
  }

其實也是很好理解的,遍歷了runningAsyncCalls,記錄同一個Host的個數。

現在來看一個AsyncCall的原始碼,這塊基本上是核心執行的地方了。

final class AsyncCall extends NamedRunnable {
        。。。
  }

看一個類,首先看一下這個類的結構,可以看到AsyncCall繼承了NameRunnable類。

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是一個抽象類,首先了Runnable介面,這就很好理解了,接著看run方法,可以看到,這裡將當前執行的執行緒的名字設為我們在構造方法中傳入的名字,接著執行execute方法,finally再設定回來。所以現在我們理所當然的回到AsyCall找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);
      }
    }

終於,找到了Response的身影,那麼就意味著執行網路請求就在getResponseWithInterceptorChain()方法中,後面的程式碼其實基本上就是一些介面回撥,回調當前Call的執行狀態,這裡就不分析了,這裡我們重點看一下getResponseWithInterceptorChain()這個方法,給我的感覺這個方法就是okHttp的精髓。

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    //失敗和重定向過濾器
    interceptors.add(retryAndFollowUpInterceptor);
    //封裝request和response過濾器
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //快取相關的過濾器,負責讀取快取直接返回、更新快取
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //負責和伺服器建立連線
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      //配置 OkHttpClient 時設定的 networkInterceptors
      interceptors.addAll(client.networkInterceptors());
    }
    //負責向伺服器傳送請求資料、從伺服器讀取響應資料(實際網路請求)
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

可以看到,這裡首先new了一個Interceptor的ArrayList,然後分別加入了各種各樣的Interceptor,所以當我們預設建立okHttpClient時,okHttp預設會給我們實現這些過濾器,每個過濾器執行不同的任務,這個思想太屌了有木有,每個過濾器負責自己的任務,各個過濾器間相互不耦合,高內聚,低耦合,對拓展放開巴拉巴拉等一系列設計思想有木有,這裡可以對比一下Volley原始碼中的思想,Volley的處理是將快取,網路請求等一系列操作揉在一起寫,導致使用者對於Volley的修改只能通過修改原始碼方式,而修改就必須要充分閱讀理解volley整個的流程,可能一部分的修改會影響全域性的流程,而這裡,將不同的職責的過濾器分別單獨出來,使用者只需要對關注的某一個功能項進行理解,並可以進行擴充修改,一對比,okHttp在這方面的優勢立馬體現出來了。這裡大概先描述一下幾個過濾器的功能:

retryAndFollowUpInterceptor——失敗和重定向過濾器
BridgeInterceptor——封裝request和response過濾器
CacheInterceptor——快取相關的過濾器,負責讀取快取直接返回、更新快取
ConnectInterceptor——負責和伺服器建立連線,連線池等
networkInterceptors——配置 OkHttpClient 時設定的 networkInterceptors
CallServerInterceptor——負責向伺服器傳送請求資料、從伺服器讀取響應資料(實際網路請求)

新增完過濾器後,就是執行過濾器了,這裡也很重要,一開始看比較難以理解。

Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);

可以看到這裡建立了一個RealInterceptorChain,並呼叫了proceed方法,這裡注意一下0這個引數。

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, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    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");
    }

    if (response.body() == null) {
      throw new IllegalStateException(
          "interceptor " + interceptor + " returned a response with no body");
    }

    return response;
  }

第一眼看,腦袋可能會有點發麻,稍微處理一下。

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;
    。。。

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

    。。。

    return response;
  }

這樣就很清晰了,這裡index就是我們剛才的0,也就是從0開始,如果index超過了過濾器的個數丟擲異常,後面會再new一個RealInterceptorChain,而且會將引數傳遞,並且index+1了,接著獲取index的interceptor,並呼叫intercept方法,傳入新new的next物件,這裡可能就有點感覺了,這裡用了遞迴的思想來完成遍歷,為了驗證我們的想法,隨便找一個interceptor,看一下intercept方法。

public final class ConnectInterceptor implements Interceptor {
  public final OkHttpClient client;

  public ConnectInterceptor(OkHttpClient client) {
    this.client = client;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    。。。暫時沒必要看。。。

    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }
}

可以看到這裡我們拿了一個ConnectInterceptor的原始碼,這裡得到chain後,進行相應的處理後,繼續呼叫proceed方法,那麼接著剛才的邏輯,index+1,獲取下一個interceptor,重複操作,所以現在就很清楚了,這裡利用遞迴迴圈,也就是okHttp最經典的責任鏈模式

4.同步請求

非同步看完,同步其實就很好理解了。

/**
   * 同步請求
   */
  @Override public Response execute() throws IOException {
      //檢查這個call是否執行過
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
      //回撥
    eventListener.callStart(this);
    try {
        //將請求加入到同步佇列中
      client.dispatcher().executed(this);
        //建立過濾器責任鏈,得到response
      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);
    }
  }

可以看到基本上流程都一致,除了是同步執行,核心方法走的還是getResponseWithInterceptorChain()方法。

okHttp流程

到這裡okHttp的流程基本上分析完了,接下來就是對Inteceptor的分析了,這裡獻上一張偷來的圖便於理解流程,希望能分析完所有的Inteceptor吧!
OkHttp原始碼