OkHttp源碼分析(一) 請求和響應過程簡單分析

這篇文章簡要介紹OkHttp的請求和響應過程。

文章基於OkHttp3.14.3版本java

0 前言

OkHttp做爲當下Java系編程的網絡請求庫,其熱門程度自沒必要說了。網上有關OkHttp的使用、封裝和源碼分析的文章和代碼也早已經是百家齊放、甚至能夠說是爛大街了。然而儘管如此,筆者仍是但願可以將對OkHttp的學習和研究記錄下來造成本身的內容,方便之後查看,因而開始寫吧,好記性不如爛筆頭。git

​ 那就從最簡單的開始。這篇文章打算簡要描述一下OkHttp中大體的請求響應過程。github

1 開始

​ 首先看看簡單的同步GET請求和異步GET請求:web

  • 同步GET請求
public static void getSync() {
    // Step 1. 建立一個HttpClient實例用於建立請求任務
    OkHttpClient httpClient = new OkHttpClient();
    // Step 2. 構建一個Request用於封裝請求地址、請求類型、參數等信息
    Request request = new Request.Builder().get()
                                           .url("https://www.baidu.com")
                                           .build();
    // Step 3. 建立一個新的請求任務Call
    Call call = httpClient.newCall(request);
    try {
        // Step 4. 發起請求
        Response response = call.execute();
        // Step 5. 讀取、處理請求結果
        ResponseBody responseBody = response.body();
        if (responseBody != null) {
            System.out.println(responseBody.string());
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
  • 異步GET請求:
public void getAsync() {
    // Step 1. 建立一個HttpClient實例用於建立請求任務
    OkHttpClient httpClient = new OkHttpClient();
    // Step 2. 構建一個Request用於封裝請求地址、請求類型、參數等信息
    Request request = new Request.Builder().get()
                                           .url("https://www.baidu.com")
                                           .build();
    // Step 3. 建立一個新的請求任務Call
    Call call = httpClient.newCall(request);
    // Step 4. 發起請求
    call.enqueue(new Callback() {
        @Override
        public void onFailure(final Call call, final IOException e) {
            e.printStackTrace();
        }

        @Override
        public void onResponse(final Call call, final Response response) throws IOException {
            // Step 5. 讀取、處理請求結果
            ResponseBody responseBody = response.body();
            if (responseBody != null) {
                System.out.println(responseBody.string());
            }
        }
    });

}

能夠看到,無論是同步仍是異步請求,都須要通過Step1~Step3三個步驟構建一個請求任務,並經過調用call.execute()/call.enqueue(callback)來執行同步/異步請求。那接下來就看看這兩個方法的執行過程吧。編程

首先看看call.execute()緩存

點擊查看Call類,發現Call是一個接口,嘗試跳轉到call.execute()方法的具體實現(ps: AS快捷鍵Ctrl+Alt+B實現快速跳轉到方法的具體實現),來到Call接口的惟一實現類RealCall類,RealCall.execute()具體實現以下:服務器

@Override public Response execute() throws IOException {
  // Step 1.
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  // Step 2.
  transmitter.timeoutEnter();
  transmitter.callStart();
  try {
    // Step 3.
    client.dispatcher().executed(this);
    // Step 4.
    return getResponseWithInterceptorChain();
  } finally {
    // Step 5.
    client.dispatcher().finished(this);
  }
}
  • Step 1.執行請求以前,確保該call請求任務還沒有被執行過。由此能夠看到OkHttp要求每一個請求任務只能被執行一次。
  • Step 2.開始計算超時時間,並記錄請求開始callStart這個流程。
  • Step 3.經過Dispatcher將本次請求任務記錄到同步請求隊列runningSyncCalls。runningSyncCalls是一個實現爲ArrayDeque的雙向隊列:
/** 正在運行的同步請求任務,包括還沒有結束就已經取消同步請求. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
synchronized void executed(RealCall call) {
  runningSyncCalls.add(call);
}
  • Step 4. 接着調用RealCall.getResponseWithInterceptorChain()發起請求並獲取請求結果;
  • Step 5.在返回結果給上層調用以前,經過Dispatcher將本次任務從runningSyncCalls隊列中移除以表示本次請求任務結束:
  • Step 6.返回請求結果給上層,請求結束。

通過以上這幾個步驟,一次同步GET請求就算是結束了。能夠發現,RealCall.getResponseWithInterceptorChain()方法負責進行具體的HTTP請求,這裏暫時不跟進去,先來看看異步的GET請求websocket

Ctrl+Alt+B快速跳轉到call.enqueue(callback)的具體實現RealCall.enqueue(callback):cookie

@Override public void enqueue(Callback responseCallback) {
  // Step 1.
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  // Step 2.
  transmitter.callStart();
  // Step 3.
  client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
  • Step 1.與同步GET請求的Step 1相同。
  • Step 2.記錄該請求過程。
  • Step 3.構造一個AsyncCall對象並加入請求隊列。AsyncCall是RealCall的內部類,用於表示異步的請求任務。AsyncCall的父類NamedRunnable是一個實現了Runnable接口的抽象類並重寫了Runnable.run方法,同時對外提供了execute()抽象方法,並在run方法實現中調用。所以,接下來關注AsyncCall類實現的execute()方法,既然AsyncCall是一個Runnable,而run中又調用了execute(),能夠斷定,異步請求任務最終會執行到這裏並進行實際的HTTP請求
@Override protected void execute() {
  boolean signalledCallback = false;
  // Step 3.1 開始計算請求超時時間
  transmitter.timeoutEnter();
  try {
    // Step 3.2 發起請求並獲取請求結果
    Response response = getResponseWithInterceptorChain();
    signalledCallback = true;
    // Step 3.3 請求成功,將結果經過回調接口返回給上層
    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 {
    // Step 4.結束本次請求任務,並從隊列中移除
    client.dispatcher().finished(this);
  }
}
}

而查閱execute的實現也驗證了這個斷定是正確的,至於什麼時候,從哪兒,會執行到這個execute方法,先留個小坑吧後續再填~。同時,對比RealCall.execute()方法(同步請求)和AsyncCall.execute()方法(異步請求)發現,無論是同步仍是異步請求,最後都會經過調用getResponseWithInterceptorChain()方法來實現網絡請求求並獲取返回結果,看看這個方法的實現:網絡

Response getResponseWithInterceptorChain() throws IOException {
  // 構建一個完整的攔截器列表
  List<Interceptor> interceptors = new ArrayList<>();
  // 添加用戶自定義的攔截器
  interceptors.addAll(client.interceptors());
  // 添加用於失敗重試和重定向的攔截器
  interceptors.add(new RetryAndFollowUpInterceptor(client));
  // 添加用於鏈接應用層和網絡層的攔截器,該攔截器會將一個用戶請求轉換爲網絡請求,並將網絡請求結果以用戶友好的方式返回
  interceptors.add(new BridgeInterceptor(client.cookieJar()));
  // 添加用於緩存的攔截器
  interceptors.add(new CacheInterceptor(client.internalCache()));
  // 添加用於打開與服務器間網絡鏈接的攔截器
  interceptors.add(new ConnectInterceptor(client));
  if (!forWebSocket) {
    // 若是本次請求任務不是websocket請求,則添加網絡攔截器,該攔截器也須要用戶自定義
    interceptors.addAll(client.networkInterceptors());
  }
  // 添加最後一個攔截器,該攔截器負責執行最終的網絡請求並返回結果
  interceptors.add(new CallServerInterceptor(forWebSocket));
    // 構建一個攔截器鏈chain對象
  Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
                                                     originalRequest, this, client.connectTimeoutMillis(),
                                                     client.readTimeoutMillis(), client.writeTimeoutMillis());

  boolean calledNoMoreExchanges = false;
  try {
    // 執行請求並獲取返回結果
    Response response = chain.proceed(originalRequest);
    if (transmitter.isCanceled()) {
      closeQuietly(response);
      throw new IOException("Canceled");
    }
    return response;
  } catch (IOException e) {
    calledNoMoreExchanges = true;
    throw transmitter.noMoreExchanges(e);
  } finally {
    if (!calledNoMoreExchanges) {
      transmitter.noMoreExchanges(null);
    }
  }
}

能夠看到,這個方法最終是經過chain.proceed(originalRequest);實現請求和返回結果,到這裏,OkHttp的請求過程就結束了。所以,一個OkHttp的請求過程大體以下:

OkHttp請求過程

先到這裏吧。原本想着一篇寫完的但太長了彷佛本身都不想看,仍是一步步來吧。下一篇開始着重分析以上OkHttp請求過程當中接觸到的各個關鍵的類。

2 The End :)

歡迎關注公衆號:
NullObject
文章首發在我的博客 https://www.nullobject.cn,公衆號 NullObject同步更新。
相關文章
相關標籤/搜索