okhttp源碼分析(一)——OkHttp的工作流程分析

如果我們想請求數(shù)據(jù)侵状,使用少量的代碼就可以實(shí)現(xiàn):

OkHttpClient client = new OkHttpClient();
String url = "https://www.baidu.com/";
Request request = new Request().Builder()
                    .url(url)
                    .get()
                    .build();
Call call = client.newCall(request);
request.enqueue(new CallBack(){
    @Override
    public void onResponse(Call call,Response response) throws IOException{
        
    }
    
    @Override
    public void onFailure(Call call,IOException e){
        
    }
})

OkHttpClient類

創(chuàng)建OkHttpClient類的兩種方式:

  • 直接創(chuàng)建對象 new OkHttpClient()
  • new OkHttpClient.Builder().build()

OkHttpClient對象源碼:

public OkHttpClient() {
this(new Builder());
}

OkHttpClient(Builder builder) {
//調(diào)度器,用于控制并發(fā)的請求。內(nèi)部保存同步和異步請求的call逛薇,并使用線程池處理異步請求看疗。
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;//代理設(shè)置
this.protocols = builder.protocols;//默認(rèn)支持http協(xié)議版本
this.connectionSpecs = builder.connectionSpecs;//okhttp連接 connection配置
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;//一個(gè)Call的狀態(tài)監(jiān)聽器
this.proxySelector = builder.proxySelector;//使用默認(rèn)的代理選擇器
this.cookieJar = builder.cookieJar;//默認(rèn)是沒有cookie的
this.cache = builder.cache;//緩存
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;//使用默認(rèn)的Scoket工廠產(chǎn)生Socket

boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
  isTLS = isTLS || spec.isTls();
}

if (builder.sslSocketFactory != null || !isTLS) {
  this.sslSocketFactory = builder.sslSocketFactory;
  this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
  X509TrustManager trustManager = Util.platformTrustManager();
  this.sslSocketFactory = newSslSocketFactory(trustManager);
  this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}

if (sslSocketFactory != null) {
  Platform.get().configureSslSocketFactory(sslSocketFactory);
}

this.hostnameVerifier = builder.hostnameVerifier;//安全相關(guān)的設(shè)置
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
    certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;//連接池
this.dns = builder.dns;//域名解析系統(tǒng) domain name->ip address
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.callTimeout = builder.callTimeout;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;//這個(gè)和websocket相關(guān)秋柄,為了保持長連接显沈,我們必須每間隔一段時(shí)間放松一個(gè)ping指令

if (interceptors.contains(null)) {
  throw new IllegalStateException("Null interceptor: " + interceptors);
}
if (networkInterceptors.contains(null)) {
  throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
}
}

Call類

在定義了請求對象后,需要生成一個(gè)Call對象志电。該對象代表一個(gè)準(zhǔn)備被執(zhí)行的請求曙咽。Call是可以被取消的,Call表示單個(gè)請求/響應(yīng)對流挑辆,不能執(zhí)行兩次例朱。

public interface Call extends Cloneable {
    
    Request request();
    
    Response execute() throws IOException;
    
    void enqueue(Callback responseCallback);
    
    void cancel();
    
    boolean isExecuted();

    boolean isCanceled();
    
    Timeout timeout();
    
    Call clone();

    interface Factory {
        Call newCall(Request request);
    }
}
  • 進(jìn)入OkHttpClient的newCall方法
public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
}
  • newRealCall
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.transmitter = new Transmitter(client, call);
    return call;
}

newCall方法獲得Call實(shí)際是RealCall孝情,RealCall就是準(zhǔn)備執(zhí)行的請求,是對接口Call的實(shí)現(xiàn)洒嗤,其內(nèi)部持有OkHttpClient實(shí)例箫荡,Request實(shí)例。并且這里還創(chuàng)建了Transmitter給RealCall的transmitter賦值渔隶。

Transmitter類

Transmitter意為發(fā)射器羔挡,是應(yīng)用層和網(wǎng)絡(luò)層的橋梁。在進(jìn)行連接间唉、真正發(fā)出請求和讀取響應(yīng)中起到很重要的作用绞灼。

public Transmitter(OkHttpClient client, Call call) {
    this.client = client;
    this.connectionPool = Internal.instance.realConnectionPool(client.connectionPool());
    this.call = call;
    this.eventListener = client.eventListenerFactory().create(call);
    this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
}

Transmitter內(nèi)部持有OkHttpClient、連接池呈野、call低矮、事件監(jiān)聽器。

Dispatcher類

Dispatcher類負(fù)責(zé)異步任務(wù)的請求策略际跪。

public final class Dispatcher {
  private int maxRequests = 64;
  //每個(gè)主機(jī)的最大請求數(shù)商佛,如果超過這個(gè)數(shù)喉钢,新的請求會被加到readyAsyncCalls隊(duì)列中
  private int maxRequestsPerHost = 5;
  private @Nullable Runnable idleCallback;
  
  //任務(wù)隊(duì)列線程池
  private @Nullable ExecutorService executorService;
  //待執(zhí)行異步任務(wù)隊(duì)例
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
  //運(yùn)行中的異步任務(wù)隊(duì)例
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
  //運(yùn)行中同步任務(wù)隊(duì)列
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

  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<>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }
}

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

client.newCall(request).execute(),execute方法在Call的實(shí)現(xiàn)類RealCall中姆打。

public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    transmitter.timeoutEnter();//超時(shí)計(jì)時(shí)開始
    transmitter.callStart();//回調(diào)監(jiān)聽器的請求開始
    try {
      client.dispatcher().executed(this);//放入隊(duì)列
      return getResponseWithInterceptorChain();//執(zhí)行請求獲取結(jié)果
    } finally {
      client.dispatcher().finished(this);//請求結(jié)束
    }
}

首先判斷 如果已經(jīng)執(zhí)行,就會拋出異常肠虽。這就是一個(gè)請求只能執(zhí)行一次的原因幔戏。然后回調(diào)請求監(jiān)聽器的請求開始。然后調(diào)用client的調(diào)度器Dispatcher的executed方法税课。

  • dispatcher().executed()
synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
}

請求放入一個(gè)雙端隊(duì)列runningSyncCalls中闲延,表示正在執(zhí)行的同步請求。

然后返回了getResponseWithInterceptorChain()的結(jié)果Response韩玩,同步請求真正的請求流程是在getResponseWithInterceptorChain方法中(詳情見下節(jié))垒玲。
最后請求結(jié)束,會走Dispatcher的finished(Deque calls, T call)方法找颓。

  • dispatcher.finished()
void finished(RealCall call) {
    finished(runningSyncCalls, call);
  }

private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      idleCallback = this.idleCallback;
    }
    
    boolean isRunning = promoteAndExecute();
    
    if (!isRunning && idleCallback != null) {
      idleCallback.run();
    }
}

這里將call從同步隊(duì)列中移除合愈,并且調(diào)用了promoteAndExecute()方法,這個(gè)方法在后面講述击狮。

異步請求執(zhí)行流程

  • 異步方法equeue()
@Override 
public void enqueue(Callback responseCallback) {
synchronized (this) {
  //設(shè)置exexuted參數(shù)為true佛析,表示不可以執(zhí)行兩次
 if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
}
transmitter.callStart();//回調(diào)請求監(jiān)聽器的請求開始
//傳入一個(gè)新的對象AsyncCall
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
  • AsyncCall類
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;

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

String host() {
  return originalRequest.url().host();
}

Request request() {
  return originalRequest;
}

RealCall get() {
  return RealCall.this;
}

@Override protected void execute() {
  boolean signalledCallback = false;
  try {
    //執(zhí)行耗時(shí)的IO操作
    //獲取攔截器鏈,詳見下篇文章
    Response response = getResponseWithInterceptorChain();
    if (retryAndFollowUpInterceptor.isCanceled()) {
      signalledCallback = true;
          
    //回調(diào)彪蓬,注意這里回調(diào)是在線程池中寸莫,而不是向當(dāng)前的主線程回調(diào)
      responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
    } else {
      signalledCallback = true;
      //回調(diào),同上
      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);
      //回調(diào)档冬,同上
      responseCallback.onFailure(RealCall.this, e);
    }
  } finally {
    
    client.dispatcher().finished(this);
  }
}
}

AsyncCall繼承NamedRunnable,NamedRunnable實(shí)現(xiàn)自Runnable,即AsyncCall就是個(gè)Runnable膘茎,它是會在線程或線程池中執(zhí)行run方法的桃纯。

  • NamedRunnable類
public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      //execute抽象方法,在AsyncCall中有具體實(shí)現(xiàn)
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

在分析同步的時(shí)候知道Dispatcher調(diào)度器負(fù)責(zé)異步請求策略辽狈,去看看equeue方法慈参。

  • Dispatcher.equeue()
void enqueue(AsyncCall call) {
synchronized (this) {
      readyAsyncCalls.add(call);
    
      // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
      // the same host.
      //相同host請求,共用一個(gè)調(diào)用技術(shù)
      if (!call.get().forWebSocket) {
        AsyncCall existingCall = findExistingCallWithHost(call.host());
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
      }
    }
    promoteAndExecute();
}

//從runningAsyncCalls和readyAsyncCalls找到相同的host請求
private AsyncCall findExistingCallWithHost(String host) {
    for (AsyncCall existingCall : runningAsyncCalls) {
      if (existingCall.host().equals(host)) return existingCall;
    }
    for (AsyncCall existingCall : readyAsyncCalls) {
      if (existingCall.host().equals(host)) return existingCall;
    }
    return null;
}
  • promoteAndExecute()
//調(diào)度的核心方法:在控制異步并發(fā)的策略基礎(chǔ)上刮萌,使用線程池 執(zhí)行異步請求
private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();
    
        if (runningAsyncCalls.size() >= maxRequests) break; //最大并發(fā)數(shù)64.
        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host最大并發(fā)數(shù).
    
        i.remove();//從等待隊(duì)列中移除
        //host并發(fā)數(shù)+1
        asyncCall.callsPerHost().incrementAndGet();
        //加入可執(zhí)行請求的集合
        executableCalls.add(asyncCall);
        //加入正在執(zhí)行的異步請求隊(duì)列
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;
    }
    
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      //可執(zhí)行的請求
      asyncCall.executeOn(executorService());
    }
    
    return isRunning;
}

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

遍歷readyAsyncCalls,先進(jìn)行兩個(gè)檢查:

  1. 正在執(zhí)行異步請求runningAsyncCalls數(shù)量大于最大并發(fā)請求數(shù)64就break;
  2. 相同host請求的數(shù)量大于5驮配,就continue。

如果檢查都通過着茸,就從等待隊(duì)列中移除壮锻,callPerHost自增1,放入可執(zhí)行的集合executableCalls涮阔,并添加到隊(duì)列runningAsyncCalls中猜绣,表示正在執(zhí)行的異步請求。

這里的異步請求等待隊(duì)列敬特,是為了控制最大并發(fā)數(shù)的緩沖掰邢,異步請求并發(fā)數(shù)達(dá)到64、相同host的異步請求達(dá)到5伟阔,都要放入等待隊(duì)列辣之。

  • AsyncCall.executeOn()
void executeOn(ExecutorService executorService) {
  assert (!Thread.holdsLock(client.dispatcher()));
  boolean success = false;
  try {
    //在線程池中執(zhí)行asyncCall
    executorService.execute(this);
    success = true;
  } catch (RejectedExecutionException e) {
    InterruptedIOException ioException = new InterruptedIOException("executor rejected");
    ioException.initCause(e);
    transmitter.noMoreExchanges(ioException);
    responseCallback.onFailure(RealCall.this, ioException);//回調(diào)失敗
  } finally {
    if (!success) {
      client.dispatcher().finished(this); //執(zhí)行發(fā)生異常 結(jié)束
    }
  }
}

AsyncCall的run方法會走到execute()方法,在上面有展示皱炉。

下面總結(jié)一下請求的流程:

  • 同步請求
  1. 調(diào)用client.newCall(request).execute()方法怀估,也就是RealCall的execute方法;
  2. execute方法內(nèi)部調(diào)用client.dispatcher().executed()方法合搅,將當(dāng)前RealCall加入到runningSyncCalls隊(duì)列多搀;
  3. 使用getResponseWithInterceptorChain()獲取結(jié)果;
  4. 最后調(diào)用Dispatcher的finish方法結(jié)束請求灾部。
  • 異步請求
  1. 調(diào)用client.newCall(request).equeue()方法康铭,其內(nèi)部調(diào)用client.dispatcher().enqueue(new AsyncCall(responseCallback))方法;
  2. 先將AsyncCall加入到當(dāng)前readyAsyncCalls隊(duì)列中,在找到執(zhí)行當(dāng)前主機(jī)的AsyncCall,一個(gè)主機(jī)用同一個(gè)AsyncCall;
  3. 使用promoteAndExecute()方法在控制異步并發(fā)的策略基礎(chǔ)上使用線程池執(zhí)行異步請求(并發(fā)控制有包括最大并發(fā)數(shù)64赌髓,host最大并發(fā)數(shù)5)从藤。異步請求的執(zhí)行也是使用getResponseWithInterceptorChain(),獲得結(jié)果后回調(diào)出去。最后調(diào)用Dispatcher的finish方法結(jié)束請求春弥。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末呛哟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子匿沛,更是在濱河造成了極大的恐慌扫责,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逃呼,死亡現(xiàn)場離奇詭異鳖孤,居然都是意外死亡者娱,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門苏揣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來黄鳍,“玉大人,你說我怎么就攤上這事平匈】蚬担” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵增炭,是天一觀的道長忍燥。 經(jīng)常有香客問我,道長隙姿,這世上最難降的妖魔是什么梅垄? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮输玷,結(jié)果婚禮上队丝,老公的妹妹穿的比我還像新娘。我一直安慰自己欲鹏,他們只是感情好机久,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著貌虾,像睡著了一般吞加。 火紅的嫁衣襯著肌膚如雪裙犹。 梳的紋絲不亂的頭發(fā)上尽狠,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機(jī)與錄音叶圃,去河邊找鬼袄膏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛掺冠,可吹牛的內(nèi)容都是我干的沉馆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼德崭,長吁一口氣:“原來是場噩夢啊……” “哼斥黑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起眉厨,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤锌奴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后憾股,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鹿蜀,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡箕慧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了茴恰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颠焦。...
    茶點(diǎn)故事閱讀 39,731評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖往枣,靈堂內(nèi)的尸體忽然破棺而出伐庭,到底是詐尸還是另有隱情,我是刑警寧澤分冈,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布似忧,位于F島的核電站,受9級特大地震影響丈秩,放射性物質(zhì)發(fā)生泄漏盯捌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一蘑秽、第九天 我趴在偏房一處隱蔽的房頂上張望饺著。 院中可真熱鬧,春花似錦肠牲、人聲如沸幼衰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渡嚣。三九已至,卻和暖如春肥印,著一層夾襖步出監(jiān)牢的瞬間识椰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工深碱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腹鹉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓敷硅,卻偏偏與公主長得像功咒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子绞蹦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評論 2 354