OkHttp

寫在開頭

文章地址:https://juejin.im/post/5e1be39b6fb9a02fcd130d1f

本文是對該文章的個(gè)人理解總結(jié),僅用于個(gè)人復(fù)習(xí),有需要看此文章即可。

大綱

  • 使用
  • 解析
  • 責(zé)任鏈模式
  • 總結(jié)

使用

最簡單的“Get”請求

// 實(shí)例客戶端
val okHttpClient = OkHttpClient()
// 構(gòu)建 request 請求參數(shù)
val request = Request.Builder().url("http://www.baidu.com").build()
//執(zhí)行同步請求并返回
val response = okHttpClient.newCall(request).execute()

解析

解析上述三步

1. OkHttpClient()
  1.第一步
  public OkHttpClient() {
    //實(shí)例 Builder,構(gòu)建者模式
    this(new Builder());
  }
  
  2.第二步
  OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    //...其他配置
  }
  
  public static final class Builder {
  Dispatcher dispatcher;
  //...其他配置

  3.第三步
  //new Builder() 會創(chuàng)建一系列默認(rèn)配置
  public Builder() {
  dispatcher = new Dispatcher();
  //...其他配置
  }
}

總結(jié)來說:實(shí)例 OkHttp(),就會實(shí)例一個(gè) Builder(),它里面會初始化一系列配置。(攔截器,請求超時(shí)等)

2. Request.Builder().url("地址").build()

顯然也是使用了“Builder”模式

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

默認(rèn)“GET”方法,這里我們再加了請求的“url”

3. okHttpClient.newCall(request)
  @Override public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
  }
  
  RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    //初始化一些配置
    ...
  }

可以看出“okHttpClient”的“newCall”實(shí)際是使用了“RealCall”類

4. execute()

最后調(diào)用“RealCall”的“execute”請求

@Override public Response execute() throws IOException {
  //同步檢查有沒有多次執(zhí)行過
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  //捕獲棧堆跟蹤,不需要理會
  captureCallStackTrace();
  try {
    //1. 添加請求到隊(duì)列
    client.dispatcher().executed(this);
    //2. 重點(diǎn),可先看最后的責(zé)任鏈的個(gè)人理解
    Response result = getResponseWithInterceptorChain();
    if (result == null) throw new IOException("Canceled");
    return result;
  } finally {
    //3. 從隊(duì)列移除請求
    client.dispatcher().finished(this);
  }
}

//1. 添加請求到隊(duì)列,從 okHttpClient 拿到 dispatcher( Dispatcher 類)
//   dispatcher 在實(shí)例 Buidler 時(shí)初始化了
//   runningSyncCall 是 一個(gè)雙端隊(duì)列 Deque
synchronized void executed(RealCall call) {
  runningSyncCalls.add(call);
}

//3. 從隊(duì)列移除請求,沒有這個(gè) call 就報(bào)錯(cuò)
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!");
    ...
  }
  ...
}

//2. 重點(diǎn),使用“責(zé)任鏈模式”獲取請求結(jié)果
Response getResponseWithInterceptorChain() throws IOException {
  //實(shí)例 list 存放一些列攔截器
  List<Interceptor> interceptors = new ArrayList<>();
  //將自己在 OkHttpClient 添加的攔截器放入,這里是我沒有添加
  interceptors.addAll(client.interceptors());
  //重試并跟蹤攔截器,負(fù)責(zé)失敗的重試及重定向
  interceptors.add(retryAndFollowUpInterceptor);
  //負(fù)責(zé)添加必要的“Header”,接收響應(yīng)式,移除必要的“Header”
  interceptors.add(new BridgeInterceptor(client.cookieJar()));
  //負(fù)責(zé)緩存的讀取,更新
  interceptors.add(new CacheInterceptor(client.internalCache()));
  //負(fù)責(zé)和服務(wù)器的連接
  interceptors.add(new ConnectInterceptor(client));
  //若不是 webSocket 請求,默認(rèn)是false
  if (!forWebSocket) {
    // okHttpClient 設(shè)置的網(wǎng)絡(luò)攔截器 ,這里也沒有設(shè)置
    interceptors.addAll(client.networkInterceptors());
  }
  //負(fù)責(zé)向服務(wù)器發(fā)送請求數(shù)據(jù),從服務(wù)器相應(yīng)數(shù)據(jù)
  interceptors.add(new CallServerInterceptor(forWebSocket));
  //責(zé)任鏈實(shí)例,RealInterceptorChain 實(shí)例
  Interceptor.Chain chain = new RealInterceptorChain(
      interceptors, null, null, null, 0, originalRequest);
  //開始責(zé)任鏈模式調(diào)用
  return chain.proceed(originalRequest);
}

5. chain.proceed(originalRequest)
// RealInterceptorChain 的 proceed 方法
@Override public Response proceed(Request request) throws IOException {
  //這里的 streamAllocation, httpCodec, connection 開始是空的
  //但經(jīng)過責(zé)任鏈的調(diào)用,將攔截器的結(jié)果返回來后就不為空
  return proceed(request, streamAllocation, httpCodec, connection);
}

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
    RealConnection connection) throws IOException {
  //若 index 大于等于攔截器集合 就報(bào)錯(cuò)
  //開始為零,其余的都是其他攔截器責(zé)任鏈模式傳來的索引
  //看下面的 RealInterceptorChain next = new RealInterceptorChain
  if (index >= interceptors.size()) throw new AssertionError();
  //標(biāo)記,一般都是 ++ 后為1, 除非同一個(gè)Chain實(shí)例被調(diào)用了多次 proceed
  calls++;

  //一開始 httpCode,connection為 null
  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");
  }
  // httpCodec同上,calls:大于 1 表示自己多次調(diào)用了 proceed
  if (this.httpCodec != null && calls > 1) {
    throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
        + " must call proceed() exactly once");
  }
  // 實(shí)例下一個(gè)攔截器 (參數(shù):index + 1)
  RealInterceptorChain next = new RealInterceptorChain(
      interceptors, streamAllocation, httpCodec, connection, index + 1, request);
  Interceptor interceptor = interceptors.get(index);
  //調(diào)用當(dāng)前攔截器的 interceptor方法,傳入下一個(gè)攔截器責(zé)任鏈實(shí)例
  //該攔截器處理到處理不了的時(shí)候,就通過這個(gè)責(zé)任鏈的next.processd 回到該方法
  //簡單來說:有必要就通過這個(gè)實(shí)例叫下一個(gè)攔截器做事
  Response response = interceptor.intercept(next);

  if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
    throw new IllegalStateException("network interceptor " + interceptor
        + " must call proceed() exactly once");
  }

  if (response == null) {
    throw new NullPointerException("interceptor " + interceptor + " returned null");
  }
  return response;
}

每一個(gè)攔截器的 intercept 方法里都有一個(gè) chain.proceed,除了集合添加的最后一個(gè) CallServerInterceptor,因?yàn)樗鬀]有攔截器了。

在 CallServerInterceptor 返回 response 后,其他攔截器再處理完 自己chain.proceed后的方法,最終會返回一個(gè)完整的 response

舉例看第一個(gè)攔截器 “RetryAndFollowUpInterceptor”,這個(gè)攔截器就組裝了 StreamAllocation streamAllocation參數(shù)

  //Chain:這里是下一個(gè)攔截器的責(zé)任鏈
  @Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    //實(shí)例 StreamAllocation
    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()), callStackTrace);

      ...
      Response response = null;
      boolean releaseConnection = true;
      try {
        //將 streamAllocation 也返回給下一個(gè)攔截器處理
        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
      ...
      } 
      ...
  }

責(zé)任鏈模式

我能做的我做,不能的交給下一個(gè)人做

比如(這例子是模仿其他作者的舉例寫的,但實(shí)在忘記在哪看的 - -):

主題:公司活動(dòng),需要申請 500元 經(jīng)費(fèi)。

公司審批經(jīng)費(fèi)的額度根據(jù)職位不同而不同,部門經(jīng)理只能批200元,總經(jīng)理能批1000元。這時(shí)我交給了部門經(jīng)理審批,部門經(jīng)理看數(shù)目后提交給總經(jīng)理,總經(jīng)理確定簽字。

這就是責(zé)任鏈,能做的就做,不行的就給下一個(gè)人處理,自己不再負(fù)責(zé),當(dāng)事人不清楚是誰最終完成了簽字。

交給別人后自己不再負(fù)責(zé),這也是純種的責(zé)任鏈模式,但純種的責(zé)任鏈模式在實(shí)際需求中比較少。

實(shí)際需求中的責(zé)任鏈,一般都是:我先處理一些東西,發(fā)現(xiàn)有些東西處理不了,然后交給其他人繼續(xù)處理,最后將某些結(jié)果返回來再接著處理,OkHttp的責(zé)任鏈模式就是如此。

總結(jié)

上面講了同步請求,異步請求最終也會調(diào)用 getResponseWithInterceptorChain() 組裝數(shù)據(jù),總體流程圖如下,

流程圖

注:原文里面還有一些攔截器的詳解

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

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

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