Okhttp框架源碼分析

1.OkHttp的簡單使用

一般情況下,對于網(wǎng)絡框架有兩種常見的使用場景,同步請求和異步請求肖抱。
同步請求:

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("http://www.baidu.com").build();
Call call = client.newCall(request);
Response response = call.execute();

異步請求:

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("http://www.baidu.com").build();
client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {

    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {

    }
});

上面代碼比較簡單奋刽,并不是本文的重點瓦侮。旨在拋磚引玉...

2.OkHttp內部相關類的介紹

  • OkHttpClient:Okhttp的主類,一般情況下設計成單列使用佣谐。
  • Request:OkHttp的請求類肚吏,可以分裝了請求消息頭,消息實體相關的信息
  • Response:OkHttp的響應類狭魂,分裝了相應碼罚攀,相應實體相關的信息
  • Call:對于OkHttp來說,一個call對應一次網(wǎng)絡請求操作
  • RealCall:Call接口的真正實現(xiàn)
  • AsyncCall:當執(zhí)行的時移步請求時雌澄,會轉化成AsyncCall斋泄,它實現(xiàn)Runnable接口,是對RealCall的再次封裝
  • Dispatcher:用來分發(fā)請求任務的類
  • RetryAndFollowUpInterceptor:負責重連和重定向的攔截器
  • BridgeInterceptor:橋攔截器镐牺,處理請求頭
  • CacheInterceptor:緩存攔截器
  • ConnectInterceptor:連接攔截器
  • CallServerInterceptor:服務相應攔截器

3.OkHttp內部執(zhí)行流程介紹

同步請求流程:
當一次同步請求被執(zhí)行時炫掐,會先調用RealCall的excute方法

  @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }

這個方法的作用是先對同步請求進行分發(fā),調用的Diapatcher的excuted方法睬涧,這個方法只是將請求緩存到dispatcher的runningSyncCalls的隊列集合中募胃,然后調用RealCall自己的getResponseWithInterceptorChain方法旗唁,這是整體OkHttp最為核心的方法,這個一個攔截器鏈摔认,它將一層層過濾請求逆皮,最終返回響應,然后在調用dispatcher的finished方法参袱,就是從runningSyncCalls隊列集合中將請求剔除电谣。這就是一次完整的okhttp的同步請求。

異步請求流程:
當一次異步請求被執(zhí)行時抹蚀,會先調用RealCall的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));
  }

這個方法的作用時判斷這個請求是否已經(jīng)被執(zhí)行了剿牺,如果沒有被執(zhí)行,會將這個RealCall轉化成AsyncCall對象环壤,然后把他交給dispatcher類進行分發(fā)晒来,dispather對象中有兩個隊列集合跟異步請求有關系。

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

這兩個隊列集合從名字上就可以看出一個是準備去執(zhí)行的異步請求郑现,一個時正在執(zhí)行中的異步請求湃崩。dsipather的enqueue方法會對異步請求做進一步處理

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

這個方法的邏輯很清晰,如果超過最大任務數(shù)量64個接箫,或者同一個域名任務數(shù)5個攒读,它將進入準備隊列,反之將進入執(zhí)行隊列辛友,交由線程池執(zhí)行薄扁。因為AsyncCall繼承了NamedRunnable類,而NamedRunnable類實現(xiàn)了Runnable接口废累,所以最終的執(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);
      }
    }

這個方法的邏輯的核心還是getResponseWithInterceptorChai這個方法,根據(jù)這個方法的返回回調Callback接口實現(xiàn)邑滨,然后在Dispatcher中的集合中剔除日缨。

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

這個就是getResponseWithInterceptorChain的方法實現(xiàn),這個就是一個責任鏈設計的攔截器鏈驼修,每個攔截器層層處理殿遂。

以上就是okhttp內部的大致使用流程。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末乙各,一起剝皮案震驚了整個濱河市墨礁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耳峦,老刑警劉巖恩静,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡驶乾,警方通過查閱死者的電腦和手機邑飒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來级乐,“玉大人疙咸,你說我怎么就攤上這事》缈疲” “怎么了撒轮?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長贼穆。 經(jīng)常有香客問我题山,道長,這世上最難降的妖魔是什么故痊? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任顶瞳,我火速辦了婚禮,結果婚禮上愕秫,老公的妹妹穿的比我還像新娘慨菱。我一直安慰自己,他們只是感情好戴甩,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布抡柿。 她就那樣靜靜地躺著,像睡著了一般等恐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上备蚓,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天课蔬,我揣著相機與錄音,去河邊找鬼郊尝。 笑死二跋,一個胖子當著我的面吹牛,可吹牛的內容都是我干的流昏。 我是一名探鬼主播扎即,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼况凉!你這毒婦竟也來了谚鄙?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤刁绒,失蹤者是張志新(化名)和其女友劉穎闷营,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡傻盟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年速蕊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娘赴。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡规哲,死狀恐怖,靈堂內的尸體忽然破棺而出诽表,到底是詐尸還是另有隱情唉锌,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布关顷,位于F島的核電站糊秆,受9級特大地震影響,放射性物質發(fā)生泄漏议双。R本人自食惡果不足惜痘番,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望平痰。 院中可真熱鬧汞舱,春花似錦、人聲如沸宗雇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赔蒲。三九已至泌神,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舞虱,已是汗流浹背欢际。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留矾兜,地道東北人损趋。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像椅寺,于是被迫代替她去往敵國和親浑槽。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內容