源碼解析一次OKHttp請求的過程

OkHttp這個庫有多優(yōu)秀作為Androider大家心里都明白咬扇,應該說合格的開發(fā)者都認識它。那么耸成,這里簡單看個OKHttp的接口請求姿勢:

OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
        .url("https://www.xxxx.com/xxx/xxx/xxx")
        .build();
Call call = okHttpClient.newCall(request);
//開始執(zhí)行異步接口請求
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        // 這里接口請求失敗
    }
    @Override
    public void onResponse(Call call, Response response) throws IOException {
        // 這里接口請求成功
    }
});

所以很明顯了弄懂了上面幾個類的是干嘛、上個幾個流程做了什么自然就明白了OKHttp的執(zhí)行流程了,那么就開始分析吧堆生。

OKHttp請求中需要的對象

Request這個類是典型的'Builder'設計模式,看一眼他的成員屬性就知道這個類是干嘛的了雷酪。

public final class Request {
  final HttpUrl url;
  final String method;
  final Headers headers;
  final @Nullable RequestBody body;
  final Object tag;
  .
  .
  .
  }

看看變量名就知道了這個類封裝那些玩意淑仆,還是很愉快的完成了一小步。

OkHttpClient這個類類似個Manager類哥力,里面的成員屬性自然也比較多了蔗怠。
我們重點關注Dispatcher這個線程池屬性:

  // 可以傳入線程池,從而自定義OKHttp線程池 
  public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }

  public Dispatcher() {
  }
  // 初始化線程池 
  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;
  }

所以Dispatcher就是一個線程池管理類吩跋,用于分發(fā)每一次的Http請求寞射。

Call是接口真正的實現(xiàn)類是RealCall,而RealCall有個內(nèi)部類叫AsyncCall是個接口锌钮,最終加入Dispatcher線程池的就是AsyncCall對象桥温。

執(zhí)行Http請求操作

我們從okHttpClient.newCall操作開始閱讀:

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

所以這里封裝了一個RealCall對象然后執(zhí)行enqueue方法,我們進入該方法看看一下代碼:

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

再看最后一行代碼會執(zhí)行Dispatcher.enqueue方法梁丘,同樣的進入看看代碼怎么執(zhí)行的:

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

由于AsyncCall是實現(xiàn)了NamedRunnable接口侵浸,所以線程池就會執(zhí)行NamedRunnable.run方法,這樣子自然就執(zhí)行到AsyncCall.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 {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

我們可以看到這里直接通過getResponseWithInterceptorChain()方法直接得到Response然后通過Callback回調(diào)數(shù)據(jù)回去掏觉。

攔截器Interceptor

通過上面我們知道已經(jīng)得到Response基本上可以說已經(jīng)結束了一個流程,這里并不細節(jié)的解釋每個攔截器的代碼值漫,只是介紹一下攔截器的調(diào)用時序澳腹,所以仍然先看一下getResponseWithInterceptorChain方法:

  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

我們可以看到系統(tǒng)已經(jīng)為我們定義幾個默認的攔截器,他們的作用一起調(diào)用時序如下:


WechatIMG61.png

這里知道了調(diào)用時序,那從什么時候觸發(fā)調(diào)用遵湖?

Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);

我們可以看到是先封裝一個RealInterceptorChain對象悔政,然后執(zhí)行proceed方法自然就會執(zhí)行每一個interceptor.intercept方法。OKHttp的所有精髓之一就是攔截器的使用..

最后延旧,這里我們也可以明白一點addInterceptor / addNetworkInterceptor兩者之間有何不同了谋国,因為他們倆的調(diào)用時序不一樣。

更多攔截器的分析迁沫,將在未來找時間分析芦瘾。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市集畅,隨后出現(xiàn)的幾起案子近弟,更是在濱河造成了極大的恐慌,老刑警劉巖挺智,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祷愉,死亡現(xiàn)場離奇詭異,居然都是意外死亡赦颇,警方通過查閱死者的電腦和手機二鳄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來媒怯,“玉大人订讼,你說我怎么就攤上這事∩劝” “怎么了欺殿?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鳖敷。 經(jīng)常有香客問我脖苏,道長,這世上最難降的妖魔是什么哄陶? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任帆阳,我火速辦了婚禮,結果婚禮上屋吨,老公的妹妹穿的比我還像新娘。我一直安慰自己山宾,他們只是感情好至扰,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著资锰,像睡著了一般敢课。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天直秆,我揣著相機與錄音濒募,去河邊找鬼。 笑死圾结,一個胖子當著我的面吹牛瑰剃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播筝野,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼晌姚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了歇竟?” 一聲冷哼從身側響起挥唠,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎焕议,沒想到半個月后宝磨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡盅安,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年唤锉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宽堆。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡腌紧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出畜隶,到底是詐尸還是另有隱情壁肋,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布籽慢,位于F島的核電站浸遗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏箱亿。R本人自食惡果不足惜跛锌,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望届惋。 院中可真熱鬧髓帽,春花似錦、人聲如沸脑豹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘩欺。三九已至必盖,卻和暖如春拌牲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背歌粥。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工塌忽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人失驶。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓土居,卻偏偏與公主長得像,于是被迫代替她去往敵國和親突勇。 傳聞我的和親對象是個殘疾皇子装盯,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

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