Android OkHttp源碼解析入門教程(一)

概述

從OkHttp問(wèn)世以來(lái)秒裕,度娘速梗,google上關(guān)于OkHttp的講解說(shuō)明數(shù)不勝數(shù)籍茧,各種解讀思想不盡相同版述,一千個(gè)讀者就有一千個(gè)哈默雷特。本篇文章從源碼出發(fā)向你介紹Okhttp的基本使用以及底層實(shí)現(xiàn)原理寞冯,讓你從會(huì)寫轉(zhuǎn)向會(huì)用渴析,學(xué)習(xí)Android頂尖源碼的設(shè)計(jì)理念和開源擴(kuò)展性晚伙,如果解讀有誤,還望提出探討糾正俭茧。

工欲善其事咆疗,必先利其器

Android API23(6.0)版本以后,Google正式移除Apache-HttpClient 恢恼。OkHttp 作為一個(gè)現(xiàn)代民傻,快速,高效的HttpClient场斑,其功能之強(qiáng)大也是顯而易見的

1.支持SPDY 可以合并多個(gè)請(qǐng)求到同一個(gè)主機(jī)的請(qǐng)求漓踢、連接池、GZIP和HTTP緩存

2.支持HTTP/2協(xié)議漏隐,通過(guò)HTTP/2 可以讓客戶端中到服務(wù)器的所有請(qǐng)求共用同一個(gè)Socket連接

3.非HTTP/2 請(qǐng)求時(shí)喧半, OkHttp內(nèi)部會(huì)維護(hù)一個(gè)線程池,通過(guò)線程池可以對(duì)HTTP/1.x的連接進(jìn)行復(fù)用青责,減少延遲

4.支持post,get請(qǐng)求挺据,基于http的文件上傳和下載

5.默認(rèn)情況下,OkHttp會(huì)自動(dòng)處理常見的網(wǎng)絡(luò)問(wèn)題脖隶,像二次連接扁耐、SSL的握手問(wèn)題

當(dāng)然OkHttp的功能遠(yuǎn)不止這些,這里只是說(shuō)明平時(shí)經(jīng)常用到的产阱。既然OkHttp已經(jīng)作為官方庫(kù)使用婉称,相比我們?cè)谧鲰?xiàng)目的時(shí)候也會(huì)用,但對(duì)于其底層的實(shí)現(xiàn)原理還是一知半解构蹬,那我們就從這篇文章開始解釋其底層實(shí)現(xiàn)原理王暗。開車前先來(lái)一波介紹:

Ecplise引用:下載最新的Jar

Android studio引用:implementation 'com.squareup.okhttp3:okhttp:3.11.0' //最新版本號(hào)請(qǐng)關(guān)注okhttp官網(wǎng)

Maven引用:

<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
  <version>3.11.0</version> //最新版本號(hào)請(qǐng)關(guān)注okhttp官網(wǎng)
</dependency>

各位老司機(jī)們,馬上開車庄敛,嘀嘀嘀俗壹!

一、基本使用方法

流程如下


// 啟動(dòng)客戶端類藻烤,主要有兩種方法進(jìn)行創(chuàng)建绷雏,new對(duì)象和Builder內(nèi)部類實(shí)現(xiàn)實(shí)例化
OkHttpClient  client = new OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();

// get請(qǐng)求
// 通過(guò)Builder模式創(chuàng)建一個(gè)Request對(duì)象(即請(qǐng)求報(bào)文)
// 這里可以設(shè)置請(qǐng)求基本參數(shù):url地址,get請(qǐng)求怖亭,POST請(qǐng)求之众,請(qǐng)求頭,cookie參數(shù)等
Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .header("User-Agent", "xxx.java")
                .addHeader("token", "xxx")
                .get()
                .build();

// POST請(qǐng)求
// 表單形式上傳
RequestBody body = new FormBody.Builder().add("xxx","xxx").build();
// JSON參數(shù)形式依许,F(xiàn)ile對(duì)象上傳
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json); 
RequestBody body = RequestBody.create(MediaType.parse("File/*"), file);

Request request = new Request.Builder()
                .post(body)
                .url(url)
                .header("User-Agent", "xxx.java")
                .addHeader("token", "xxx")
                .build();


// 創(chuàng)建Call對(duì)象(Http請(qǐng)求) 棺禾,即連接Request和Response的橋梁
// newCall方法將request封裝成Call對(duì)象
Call call = client.newCall(request);
try{
// Response即響應(yīng)報(bào)文信息,包含返回狀態(tài)碼峭跳,響應(yīng)頭膘婶,響應(yīng)體等
    Response response = call.execute();
// 這里深入一點(diǎn)缺前,Call其實(shí)是一個(gè)接口,調(diào)用Call的execute()發(fā)送同步請(qǐng)求其實(shí)是調(diào)用了Realcall實(shí)現(xiàn)類的方法悬襟,Realcall從源碼可以看出示一個(gè)Runable
    System.out.println(response.body().string());
}catch(IOException e){
    e.printStackTrace();
}

看完代碼你可能覺得OkHttp基本流程很繁瑣衅码,但是去掉一些擴(kuò)展參數(shù),你會(huì)發(fā)現(xiàn)OkHttp的使用其實(shí)很簡(jiǎn)單脊岳,無(wú)非就是
1.創(chuàng)建一個(gè)OkHttpClient并實(shí)例化逝段,可設(shè)置相關(guān)參數(shù)連接時(shí)長(zhǎng)connectTimeout等
2.創(chuàng)建一個(gè)Request對(duì)象并實(shí)例化,可設(shè)置網(wǎng)絡(luò)地址url割捅,請(qǐng)求方式get,post奶躯,攜帶參數(shù)等;
3.創(chuàng)建一個(gè)Call對(duì)象亿驾,通過(guò)okhttpClient的newCall()方法將Request封裝成Call對(duì)象
4.創(chuàng)建一個(gè)Response響應(yīng)嘹黔,用于接收服務(wù)器返回的相關(guān)信息;
即OkHttpClient客戶端通過(guò)newCall()方法接受你的Request請(qǐng)求并生成Response響應(yīng)

二莫瞬、同步和異步請(qǐng)求

看到這里你可能會(huì)問(wèn)儡蔓,為什么不繼續(xù)講些關(guān)于文件上傳,文件下載疼邀,Interceptors攔截器這些內(nèi)容喂江?其實(shí)同步和異步請(qǐng)求的實(shí)現(xiàn)可以說(shuō)是源碼中非常重要的一環(huán),涉及到線程池旁振,Dispatch調(diào)度开呐,反向代理等,掌握核心科技规求,剩下的都是開胃小菜。

1)基本使用方法

同步請(qǐng)求方法

OkhttpClient  client = new OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .get()
                .build();
Call call = client.newCall(request);
try{
    Response response = call.execute();//調(diào)用同步請(qǐng)求
    System.out.println(response.body().string());
}catch(IOException e){
    e.printStackTrace();  
}

通過(guò)上面代碼可以看出同步請(qǐng)求的基本流程:
1.創(chuàng)建OkHttpClient和Request對(duì)象
2.將Request封裝成Call對(duì)象
3.調(diào)用Call的excute()發(fā)起同步請(qǐng)求

*特別注意*:當(dāng)前線程發(fā)送同步請(qǐng)求后卵惦,就會(huì)進(jìn)入阻塞狀態(tài)阻肿,直到數(shù)據(jù)有響應(yīng)才會(huì)停止(和異步最大的不同點(diǎn))

異步請(qǐng)求方法

OkhttpClient  client = new OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .get()
                .build();
Call call = client.newCall(request);
call.enqueue(new Callback() { //調(diào)用異步請(qǐng)求,CallBack用于請(qǐng)求結(jié)束以后來(lái)進(jìn)行接口回調(diào)
        @Override
        public void onFailure(Call call, IOException e) { System.out.println("Failure");}
        @Override
        public void onResponse(Call call, Response response) throw s IOException {
           System.out.println(response.body().string());
        }
    });

通過(guò)上面代碼可以看出異步請(qǐng)求的基本流程:
1.創(chuàng)建OkHttpClient和Request對(duì)象
2.將Request封裝成Call對(duì)象
3.調(diào)用Call的enqueue發(fā)起異步請(qǐng)求

*特別注意*: onFailure和onResponse都是執(zhí)行在子線程

不難看出,其實(shí)異步和同步請(qǐng)求的不同點(diǎn)在于
1沮尿、發(fā)起請(qǐng)求方法調(diào)用 2丛塌、是否阻塞線程

到此,我們已經(jīng)熟悉了OkHttp的同步畜疾,異步請(qǐng)求方法的基本使用赴邻;不管同步還是異步的調(diào)用都需要先初始化OkHttpClient,創(chuàng)建Request ,調(diào)用OkHttpClient.newcall()封裝Call對(duì)象啡捶,但其內(nèi)部又是如何實(shí)現(xiàn)的呢姥敛?


同步請(qǐng)求執(zhí)行流程

第一步:初始化OkHttpClient(Builder builder)

    public Builder() {
      dispatcher = new Dispatcher(); // 調(diào)度分發(fā)器(核心之一)
      protocols = DEFAULT_PROTOCOLS; // 協(xié)議
      connectionSpecs = DEFAULT_CONNECTION_SPECS; // 傳輸層版本和連接協(xié)議
      eventListenerFactory = EventListener.factory(EventListener.NONE); // 監(jiān)聽器
      proxySelector = ProxySelector.getDefault(); // 代理選擇器
      cookieJar = CookieJar.NO_COOKIES; // cookie
      socketFactory = SocketFactory.getDefault(); // socket 工廠
      hostnameVerifier = OkHostnameVerifier.INSTANCE; // 主機(jī)名字
      certificatePinner = CertificatePinner.DEFAULT; // 證書鏈
      proxyAuthenticator = Authenticator.NONE; // 代理身份驗(yàn)證
      authenticator = Authenticator.NONE; // 本地省份驗(yàn)證
      connectionPool = new ConnectionPool(); // 連接池(核心之一)
      dns = Dns.SYSTEM;// 基礎(chǔ)域名
      followSslRedirects = true;// 安全套接層重定向
      followRedirects = true;// 本地重定向
      retryOnConnectionFailure = true; // 連接失敗重試
      connectTimeout = 10_000; // 連接超時(shí)時(shí)間
      readTimeout = 10_000;  // 讀取超時(shí)時(shí)間
      writeTimeout = 10_000; // 寫入超時(shí)時(shí)間
      pingInterval = 0; // 命令間隔
    }

從這里看到,其實(shí)OkHttpClient的初始化已經(jīng)幫我們配置了基本參數(shù)瞎暑,我們也可以根據(jù)自身業(yè)務(wù)需求進(jìn)行相應(yīng)的參數(shù)設(shè)置(失敗重連彤敛,添加攔截器与帆,cookie等等),一般遇到創(chuàng)建對(duì)象需要大量參數(shù)時(shí)墨榄,推薦使用Builider模式鏈?zhǔn)秸{(diào)用完成參數(shù)初始化玄糟,具體使用可以去Android源碼中的AlertDialog、Notification中詳細(xì)了解袄秩;
這里我們重點(diǎn)注意兩個(gè)核心阵翎,DispatcherConnectionPool,這兩點(diǎn)會(huì)在后面做詳細(xì)講解
Dispatcher:OkHttp請(qǐng)求的調(diào)度分發(fā)器之剧,由它決定異步請(qǐng)求在線程池中是直接處理還是緩存等待郭卫,當(dāng)然對(duì)于同步請(qǐng)求,只是將相應(yīng)的同步請(qǐng)求放到請(qǐng)求隊(duì)列當(dāng)中執(zhí)行
ConnectionPool: 統(tǒng)一管理客戶端和服務(wù)器之間連接的每一個(gè)Connection猪狈,作用在于
1)當(dāng)你的Connection請(qǐng)求的URL相同時(shí)箱沦,可以選擇是否復(fù)用;2)控制Connection保持打開狀態(tài)還是復(fù)用

第二步:創(chuàng)建 (Builder builder)

  public static class Builder {
    HttpUrl url; 
    String method;
    Headers.Builder headers;
    RequestBody body; //請(qǐng)求體
    Object tag; //標(biāo)簽

    public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder(); //Headers內(nèi)部類
    }

這個(gè)構(gòu)造方法很簡(jiǎn)單雇庙,在Request.Builder模式下默認(rèn)指定請(qǐng)求方式為GET請(qǐng)求谓形,創(chuàng)建了Headers內(nèi)部類來(lái)保存頭部信息,我們?cè)賮?lái)看build方法

 public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }

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的構(gòu)造方法就是為其初始化指定需求的請(qǐng)求方式疆前,請(qǐng)求URL寒跳,請(qǐng)求頭部信息,這樣就完成同步請(qǐng)求的前兩步

第三步:調(diào)用OkHttpClient.newcall()封裝Call對(duì)象

 /**
   * Prepares the {@code request} to be executed at some point in the future.
   * 準(zhǔn)備在將來(lái)某個(gè)時(shí)候執(zhí)行{@code請(qǐng)求}
   */
  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

上面我們也提到過(guò)竹椒,Call是一個(gè)接口童太,所以它的實(shí)際操作是在RealCall類中實(shí)現(xiàn)的

final class RealCall implements Call {
  final OkHttpClient client;
  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor; //重定向攔截器

  /**
   * There is a cycle between the {@link Call} and {@link EventListener} that makes this awkward.
   * This will be set after we create the call instance then create the event listener instance.
   */
  private EventListener eventListener;

  /** The application's original request unadulterated by redirects or auth headers. */
  final Request originalRequest;
  final boolean forWebSocket;

  // Guarded by this.
  private boolean executed;

//實(shí)際構(gòu)造方法
  private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

  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其實(shí)是持有之前初始化好的OkHttpClient和Request對(duì)象胸完,同時(shí)賦值了RetryAndFollowUpInterceptor重定向攔截器书释,關(guān)于攔截器的內(nèi)容,我們會(huì)后面具體講解OKhttp內(nèi)部的5大攔截器赊窥;

第四步爆惧,調(diào)用call.exucte方法實(shí)現(xiàn)同步請(qǐng)求

 @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace(); // 捕捉異常堆棧信息
    eventListener.callStart(this); // 調(diào)用監(jiān)聽方法
    try {
      client.dispatcher().executed(this); // 調(diào)度器將call請(qǐng)求 加入到了同步執(zhí)行隊(duì)列中
      Response result = getResponseWithInterceptorChain(); // 獲取返回?cái)?shù)據(jù)
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }

首先, 加入了synchronized 同步鎖,判斷executed標(biāo)識(shí)位是否為true锨能,確保每個(gè)call只能被執(zhí)行一次不能重復(fù)執(zhí)行扯再,然后開啟了eventListener監(jiān)聽事件,接收相應(yīng)的事件回調(diào)址遇,通過(guò)dispatcher將Call請(qǐng)求添加到同步隊(duì)列中

 public Dispatcher dispatcher() {
    return dispatcher;
  }

  /** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  // 同步請(qǐng)求隊(duì)列
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

每當(dāng)調(diào)用executed同步方法時(shí)熄阻,dispather就會(huì)幫我們把同步請(qǐng)求添加到同步請(qǐng)求隊(duì)列中去,由此可以看出Dispather調(diào)度器的作用就是維持Call請(qǐng)求發(fā)送狀態(tài)維護(hù)線程池并把Call請(qǐng)求添加到相應(yīng)的執(zhí)行隊(duì)列當(dāng)中倔约,由它決定當(dāng)前Call請(qǐng)求是緩存等待還是直接執(zhí)行,流程如下

同步請(qǐng)求流程

getResponseWithInterceptorChain()是一個(gè)攔截器鏈秃殉,依次調(diào)用攔截器對(duì)返回的response進(jìn)行相應(yīng)的操作,我們?cè)谥v解到責(zé)任鏈模式時(shí)會(huì)詳細(xì)介紹,如圖


另外要特別注意下client.dispatcher().finished(this);

  /** Used by {@code Call#execute} to signal completion. */
  void finished(RealCall call) {
    finished(runningSyncCalls, call, false);  // 注意參數(shù)傳遞的值
  }

  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!");
      if (promoteCalls) promoteCalls();  // 將等待隊(duì)列的請(qǐng)求加入運(yùn)行隊(duì)列并開始執(zhí)行,只會(huì)在異 步方法中調(diào)用
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

/***********************************************************************************************************************/
  public synchronized int runningCallsCount() {
    return runningAsyncCalls.size() + runningSyncCalls.size();
  }

當(dāng)同步請(qǐng)求完成后會(huì)調(diào)用finished()方法將隊(duì)列中的請(qǐng)求清除掉复濒,runningCallsCount()計(jì)算返回正在執(zhí)行同步請(qǐng)求和正在執(zhí)行異步請(qǐng)求的數(shù)量總和脖卖,最后判斷如果runningCallsCount 為0的時(shí)候,表示整個(gè)Dispatcher分發(fā)器中沒有可運(yùn)行的請(qǐng)求巧颈,同時(shí)在滿足idleCallback不為空的情況下畦木,就調(diào)用Run方法開啟閑置接口;這里可以看出砸泛,在同步請(qǐng)求的方法中十籍,dispatcher的作用只是調(diào)用 executed將Call請(qǐng)求添加到同步隊(duì)列中,執(zhí)行完畢后調(diào)用 finished清除隊(duì)列中的請(qǐng)求唇礁,可見dispatcher更多的是為異步服務(wù)

異步請(qǐng)求執(zhí)行流程
關(guān)于OkHttpClient和Request初始化流程上文已經(jīng)講解勾栗,不清楚的可以返回去看看,所以直奔主題

第四步盏筐,調(diào)用call.enqueue方法實(shí)現(xiàn)異步請(qǐng)求

//RealCall實(shí)現(xiàn)類
  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true; // executed用于表示Call請(qǐng)求是否執(zhí)行過(guò)
    }
    captureCallStackTrace();// 捕捉異常堆棧信息
    eventListener.callStart(this);// 開啟監(jiān)聽事件
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

有沒有發(fā)現(xiàn)和同步的excute方法很類似围俘,都是先使用synchronized 防止請(qǐng)求重復(fù)執(zhí)行,然后開啟監(jiān)聽事件琢融,最后在執(zhí)行相應(yīng)的方法界牡,但奇怪的是同步在執(zhí)行完excute方法后是直接通過(guò)getResponseWithInterceptorChain()返回?cái)?shù)據(jù),異步又是如何返回?cái)?shù)據(jù)的呢漾抬?AsyncCall又是干什么的宿亡?

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        // 返回?cái)?shù)據(jù)
        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);
      }
    }
  }

這里的 AsyncCall 是 RealCall 的一個(gè)內(nèi)部類,它繼承于NamedRunnable抽象類纳令,NamedRunnable抽象類又實(shí)現(xiàn)了 Runnable挽荠,所以可以被提交到ExecutorService上執(zhí)行,在execute方法里平绩,我們看到了熟悉的流程,上文也說(shuō)到getResponseWithInterceptorChain是一個(gè)攔截器鏈圈匆,會(huì)依次執(zhí)行相應(yīng)的攔截器后返回?cái)?shù)據(jù),所以當(dāng)返回?cái)?shù)據(jù)后捏雌,通過(guò)retryAndFollowUpInterceptor重定向攔截器判斷請(qǐng)求是否正常執(zhí)行跃赚,并且通過(guò)Callback接口返回相應(yīng)數(shù)據(jù)信息,最后調(diào)用finished方法清除隊(duì)列
這里有個(gè)疑問(wèn)腹忽,Dispatcher是通過(guò)什么把異步就緒隊(duì)列的請(qǐng)求調(diào)度分發(fā)到異步執(zhí)行隊(duì)列中的?


還記得我們講client.dispatcher().finished(this)的時(shí)候,說(shuō)到過(guò)promoteCalls方法砚作,只是同步傳參的是false沒有調(diào)用窘奏,但異步傳參是true,所以promoteCalls方法才真正在異步中調(diào)用

  //Disaptcher

  /** Ready async calls in the order they'll be run. */
  // 異步就緒隊(duì)列
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  // 異步執(zhí)行隊(duì)列
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  private void promoteCalls() {
    // maxRequests最大請(qǐng)求數(shù)量64
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

源碼是不是很清楚明了葫录,原來(lái)異步隊(duì)列就是在這里進(jìn)行調(diào)度的着裹,在for循環(huán)中,Disaptcher首先對(duì)異步就緒隊(duì)列進(jìn)行遍歷米同,如果滿足runningCallsForHost(當(dāng)前調(diào)用請(qǐng)求主機(jī)數(shù))小于maxRequestsPerHost( 最大請(qǐng)求主機(jī)數(shù)5個(gè))并且異步并發(fā)數(shù)量沒有超過(guò)最大請(qǐng)求數(shù)量64的前提下骇扇,就把異步就緒隊(duì)列中最后一個(gè)元素移除加入到異步執(zhí)行隊(duì)列中



我們接著看enqueue方法具體做了哪些操作

//Disaptcher
synchronized void enqueue(AsyncCall call) {
    // 異步并發(fā)請(qǐng)求數(shù)量不能超過(guò)最大請(qǐng)求數(shù)量64
    // 當(dāng)前網(wǎng)絡(luò)請(qǐng)求的host是否小于5個(gè)請(qǐng)求的host
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      // 加入執(zhí)行隊(duì)列 并交給線程池執(zhí)行
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      // 加入就緒隊(duì)列等待
      readyAsyncCalls.add(call);
    }
  }

***************************************************************************************************************
  public synchronized ExecutorService executorService() {
   // 核心線程 最大線程 非核心線程閑置60秒回收 任務(wù)隊(duì)列
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

Disaptcher的enqueue方法只是做了一個(gè)異步請(qǐng)求的邏輯判斷摔竿,即判斷當(dāng)前異步并發(fā)執(zhí)行隊(duì)列的數(shù)量是否超過(guò)最大承載運(yùn)行數(shù)量64和相同host主機(jī)最多允許5條線程同時(shí)執(zhí)行請(qǐng)求,滿足以上條件少孝,則將傳進(jìn)來(lái)的AsyncCall添加到異步執(zhí)行隊(duì)列继低,同時(shí)啟動(dòng)線程池執(zhí)行,反之則添加到異步就緒隊(duì)列中等待稍走,executorService調(diào)用的就是AsyncCall的execute方法

同步和異步請(qǐng)求的源碼就講到這里袁翁,對(duì)過(guò)程還有不理解的可以在下方評(píng)論中提出問(wèn)題,下一篇我們接著講OkHttp的責(zé)任鏈模式婿脸,未完待續(xù)...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末粱胜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子狐树,更是在濱河造成了極大的恐慌焙压,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抑钟,死亡現(xiàn)場(chǎng)離奇詭異涯曲,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)味赃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門掀抹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人心俗,你說(shuō)我怎么就攤上這事傲武。” “怎么了城榛?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵揪利,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我狠持,道長(zhǎng)疟位,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任喘垂,我火速辦了婚禮甜刻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘正勒。我一直安慰自己得院,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布章贞。 她就那樣靜靜地躺著祥绞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蜕径,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天两踏,我揣著相機(jī)與錄音,去河邊找鬼兜喻。 笑死梦染,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的虹统。 我是一名探鬼主播弓坞,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼车荔!你這毒婦竟也來(lái)了渡冻?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤忧便,失蹤者是張志新(化名)和其女友劉穎族吻,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體珠增,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡超歌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蒂教。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巍举。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖凝垛,靈堂內(nèi)的尸體忽然破棺而出懊悯,到底是詐尸還是另有隱情,我是刑警寧澤梦皮,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布炭分,位于F島的核電站,受9級(jí)特大地震影響剑肯,放射性物質(zhì)發(fā)生泄漏捧毛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一让网、第九天 我趴在偏房一處隱蔽的房頂上張望呀忧。 院中可真熱鬧,春花似錦溃睹、人聲如沸而账。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)福扬。三九已至,卻和暖如春惜犀,著一層夾襖步出監(jiān)牢的瞬間铛碑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工虽界, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汽烦,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓莉御,卻偏偏與公主長(zhǎng)得像撇吞,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子礁叔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

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

  • 最近學(xué)習(xí)了一下okhttp的源碼牍颈,發(fā)現(xiàn)okhttp是真滴復(fù)雜。因?yàn)閛khttp是一個(gè)網(wǎng)絡(luò)請(qǐng)求庫(kù)琅关,它涉及了網(wǎng)絡(luò)請(qǐng)求的...
    有興不虛昧閱讀 618評(píng)論 0 1
  • 前幾天在在一本書上看到這樣一句話 如果僅從微觀的視角關(guān)注每一個(gè)單獨(dú)的點(diǎn)煮岁,可能會(huì)因?yàn)榭床坏秸w而迷失方向。 所以不會(huì)...
    Utte閱讀 686評(píng)論 0 0
  • 關(guān)于okhttp是一款優(yōu)秀的網(wǎng)絡(luò)請(qǐng)求框架涣易,關(guān)于它的源碼分析文章有很多画机,這里分享我在學(xué)習(xí)過(guò)程中讀到的感覺比較好的文章...
    蕉下孤客閱讀 3,605評(píng)論 2 38
  • OkHttp源碼分析 在現(xiàn)在的Android開發(fā)中呛谜,請(qǐng)求網(wǎng)絡(luò)獲取數(shù)據(jù)基本上成了我們的標(biāo)配婿奔。在早期的Android開...
    BlackFlag閱讀 326評(píng)論 0 5
  • OkHttp解析系列 OkHttp解析(一)從用法看清原理OkHttp解析(二)網(wǎng)絡(luò)連接OkHttp解析(三)關(guān)于...
    Hohohong閱讀 20,979評(píng)論 4 58