okhttp3源碼分析之請求流程

前言

在Android開發中,當下最火的網絡請求框架莫過於okhttp和retrofit,它們都是square公司的產品,兩個都是很是優秀開源庫,值得咱們去閱讀它們的源碼,學習它們的設計理念,但其實retrofit底層仍是用okhttp來發起網絡請求的,因此深刻理解了okhttp也就深刻理解了retrofit,它們的源碼閱讀順序應該是先看okhttp,我在retrofit上發現它最近的一次提交才把okhttp版本更新到3.14,okhttp目前最新的版本是4.0.x,okhttp從4.0.x開始採用kotlin編寫,在這以前仍是用java,而我本次分析的okhttp源碼版本是基本3.14.x,看哪一個版本的不重要,重要的是閱讀事後的收穫,我打算分2篇文章去分析okhttp,分別是:java

  • 請求流程(同步、異步)
  • 攔截器(Interceptor)

本文是第一篇 - okhttp的請求流程,okhttp項目地址:okhttpgit

okhttp的簡單使用

咱們經過一個簡單的GET請求來回憶一下okhttp的使用步驟,並以這個實例爲例講解okhttp的請求流程,以下:github

//一、建立OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
    .readTimeout(5, TimeUnit.SECONDS)
    .build();

//二、建立請求Request
Request request = new Request.Builder()
    .url("http://www.baidu.com")
    .build();
//三、建立一個Call,用於發起網絡請求
Call call = client.newCall(request);

//四、發起GET請求

//4.一、同步請求,調用Call的execute()方法
try {
    //接收到回覆Response
    Response response = call.execute();
    Log.d(TAG, response.body().string());
} catch (IOException e) {
    e.printStackTrace();
}

//4.二、異步請求, 調用Call的enqueue()方法
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        //接收到回覆Response
        Log.d(TAG, response.body().string());
    }
});
複製代碼

能夠看到,使用okhttp發起網絡請求要通過4步:web

  • 一、建立OkHttpClient
  • 二、建立請求Request
  • 三、經過OkHttpClient和Request建立一個Call,用於發起網絡請求
  • 四、調用Call的execute()或enqueue()方法發起同步或異步請求

當服務器處理完一個請求Request後,就會返回一個響應,在okhttp中用Response表明HTTP的響應,這就是一個典型的HTTP請求/響應流程。下面簡單介紹1~3步驟:緩存

一、建立OkHttpClient

OkHttpClient是okhttp中的大管家,它將具體的工做分發到各個子系統中去完成,它使用Builder模式配置網絡請求的各類參數如超時、攔截器、分發器等,Builder中可配置的參數以下:安全

//OkHttpClient.Builder
public static final class Builder {
    Dispatcher dispatcher;//分發器
    @Nullable Proxy proxy;//代理
    List<Protocol> protocols;//應用層協議
    List<ConnectionSpec> connectionSpecs;//傳輸層協議
    final List<Interceptor> interceptors = new ArrayList<>();//應用攔截器
    final List<Interceptor> networkInterceptors = new ArrayList<>();//網絡攔截器
    EventListener.Factory eventListenerFactory;//http請求回調監聽
    ProxySelector proxySelector;//代理選擇
    CookieJar cookieJar;//cookie
    @Nullable Cache cache;//網絡緩存
    @Nullable InternalCache internalCache;//內部緩存
    SocketFactory socketFactory;//socket 工廠
    @Nullable SSLSocketFactory sslSocketFactory;//安全套接層socket 工廠,用於HTTPS
    @Nullable CertificateChainCleaner certificateChainCleaner;//驗證確認響應證書,適用 HTTPS 請求鏈接的主機名
    HostnameVerifier hostnameVerifier;//主機名字確認
    CertificatePinner certificatePinner;//證書鏈
    Authenticator proxyAuthenticator;//代理身份驗證
    Authenticator authenticator;//本地身份驗證
    ConnectionPool connectionPool;//鏈接池,複用鏈接
    Dns dns;//域名
    boolean followSslRedirects;//安全套接層重定向
    boolean followRedirects;//本地重定向
    boolean retryOnConnectionFailure;//錯誤重連
    int callTimeout;//請求超時,它包括dns解析、connect、read、write和服務器處理的時間
    int connectTimeout;//connect超時
    int readTimeout;//read超時
    int writeTimeout;//write超時
    int pingInterval;//ping超時

    //這裏是配置默認的參數
    public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;//Protocol.HTTP_2和Protocol.HTTP_1_1
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      if (proxySelector == null) {
        proxySelector = new NullProxySelector();
      }
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool();
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      callTimeout = 0;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }

    //這裏經過另一個OkHttpClient配置參數
    Builder(OkHttpClient okHttpClient) {
      this.dispatcher = okHttpClient.dispatcher;
      this.proxy = okHttpClient.proxy;
      this.protocols = okHttpClient.protocols;
      //...
    }
    
    //...
    
    //配置完參數後,經過Builder的參數建立一個OkHttpClient
    public OkHttpClient build() {
        return new OkHttpClient(this);
    }
}
複製代碼

二、建立請求Request

在okhttp中Request表明着一個HTTP請求,它封裝了請求的具體消息,如url、header、body等,它和OkHttpClient同樣都是使用Budiler模式來配置本身的參數,以下:服務器

//Request.Budiler
public static class Builder {
    HttpUrl url;
    String method;
    Headers.Builder headers;
    RequestBody body;

    //這裏配置默認的參數
    public Builder() {
      this.method = "GET";//默認是GET請求
      this.headers = new Headers.Builder();
    }

    //這裏經過另一個Request配置參數
    Builder(Request request) {
      this.url = request.url;
      this.method = request.method;
      //...
    }
    
    //...
    
    //配置完參數後,經過Builder的參數建立一個Request
    public Request build() {
        if (url == null) throw new IllegalStateException("url == null");
        return new Request(this);
    }
}
複製代碼

三、建立用於發起網絡請求的Call

Call是一個接口,它的具體實現類是RealCall,Call中定義了一些enqueue(Callback)execute()等關鍵方法,以下:cookie

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

複製代碼

咱們看到Call接口中有一個Factory接口,Factory中有一個newCall(Request)方法,這說明Call是經過工廠模式建立,而OkHttpClient實現了Call.Factory接口,重寫了newCall(Request)方法,返回了Call的具體實現類RealCall,以下:網絡

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
    //...
    @Override 
    public Call newCall(Request request) {
        //調用了RealCall的newRealCall()
        return RealCall.newRealCall(this, request, false /* for web socket */);
    }
}

final class RealCall implements Call {
    //...
    static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
        //返回RealCall對象
        RealCall call = new RealCall(client, originalRequest, forWebSocket);
        call.transmitter = new Transmitter(client, call);
        return call;
    }
}

複製代碼

因此調用**client.newCall(request)**其實返回的是RealCall對象,而RealCall封裝了請求的調用邏輯。框架

到這裏也就走到了註釋4,也就是第4步,okhttp經過Call的實現類RealCall的execute()或enqueue()方法發起同步或異步請求,也就是本文的重點,下面分別詳細介紹:

同步請求 - RealCall :: execute()

//RealCall.java
@Override
public Response execute() throws IOException {
    //...
    try {
        //一、調用Dispatcher的executed(RealCall)方法
        client.dispatcher().executed(this);
        //二、調用getResponseWithInterceptorChain()方法
        return getResponseWithInterceptorChain();
    } finally {
        //三、同步請求任務執行完畢,調用Dispatcher的finished(RealCall)方法
        client.dispatcher().finished(this);
    }
}
複製代碼

client就是咱們上面所講的OkHttpClient的實例,它在建立RealCall時做爲構造參數傳了進去,而OkHttpClient的dispatcher()方法返回的是Dispatcher實例,它在OkHttpClient構造時被建立。

咱們先講一下Dispatcher,那Dispatcher是什麼呢?Dispatcher是一個任務調度器,它負責進行請求任務的調度,它的內部維護着3個任務隊列(readyAsyncCalls、runningAsyncCalls、runningSyncCalls)和1個線程池(executorService),Dispatcher主要內容以下:

public final class Dispatcher {
    private int maxRequests = 64;//最大請求數64個
    private int maxRequestsPerHost = 5;//每一個主機最大請求數5個
    private @Nullable Runnable idleCallback;//idle任務回調,相似於Android的idlehandler, 能夠在Dispatcher沒有任務調度(空閒時)時執行idleCallback中的任務

    //線程池,執行runningAsyncCalls隊列裏面的請求任務
    private @Nullable ExecutorService executorService;

    //等待執行的異步請求任務隊列
    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

    //正在執行的異步請求任務隊列
    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

    //正在執行的同步請求任務隊列
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

    synchronized void executed(RealCall call) {
        //...
    }

    void enqueue(AsyncCall call) {
        //...
    }  
    
    void finished(RealCall call) {
        //...
    }

    void finished(AsyncCall call) {
        //...
    }
    
    private boolean promoteAndExecute() {
        //...
    }

  //... 
}
複製代碼

Dispatcher提供了executed(RealCall)和enqueue(AsyncCall)方法來進行同步和異步請求任務的入隊,還提供了finished(RealCall)和finished(AsyncCalll)方法來進行同步和異步請求任務的出隊,能夠看到okhttp把ReadCall看成同步請求任務的表明,把AsyncCall看成異步請求任務的表明,RealCall前面已經講過了,而AsyncCal是RealCall的一個內部類,它本質上就是一個Runnable,Dispatcher的線程池執行任務主要執行的是runningAsyncCalls隊列裏面的異步請求任務,也就是AsyncCall異步任務,而Dispatcher的promoteAndExecute()方法就是用來進行異步任務的調度,它的邏輯主要是按順序把readyAsyncCalls隊列中準備執行的異步任務轉移到runningAsyncCalls後,再由線程池執行,對於同步任務Dispatcher只是暫時保存在runningSyncCalls隊列中,並不會由線程池執行。

咱們繼續回到RealCall的execute()方法,根據註釋一、二、3分爲3部分解釋同步請求流程,以下:

一、Dispatcher :: executed(RealCall)

看RealCall的execute()方法的註釋1,它首先調用了Dispatcher的executed(RealCall)方法,Dispatcher的executed(RealCall)方法實現以下:

//Dispatcher.java
synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
}
複製代碼

能夠看到沒有作什麼處理,只是簡單的把同步請求任務放入runningSyncCalls隊列。

二、RealCall :: getResponseWithInterceptorChain()

看RealCall的execute()方法的註釋2,調用getResponseWithInterceptorChain()方法,這裏纔是同步請求處理的地方,咱們點進去,以下:

//RealCall.java 
Response getResponseWithInterceptorChain() throws IOException {
    //攔截器的添加
    List<Interceptor> interceptors = new ArrayList<>();
    //添加用戶自定義攔截器
    interceptors.addAll(client.interceptors());
    //添加默認攔截器
    interceptors.add(new RetryAndFollowUpInterceptor(client));
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    //添加的最後一個攔截器是CallServerInterceptor
    interceptors.add(new CallServerInterceptor(forWebSocket));

    //建立一個RealInterceptorChain,傳入了interceptors和Request
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    try {
      //調用RealInterceptorChain的proceed(Request)方法處理請求
      Response response = chain.proceed(originalRequest);
      //...
      return response;
    } catch (IOException e) {
     //...
    } finally {
     //...
    }
  }
複製代碼

getResponseWithInterceptorChain()方法最終返回一個Response,也就是網絡請求的響應,該方法中首先把用戶自定義的攔截器和okhttp默認的攔截器封裝到一個List中,而後建立RealInterceptorChain並執行proceed(Request)方法處理請求,RealInterceptorChain的proceed(Request)方法以下:

//RealInterceptorChain.java
@Override 
public Response proceed(Request request) throws IOException {
    return proceed(request, transmitter, exchange);
 }

public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange) throws IOException {
    //...
    //再新建一個RealInterceptorChain,這裏注意index加1,
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
                                                         index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
    //獲取interceptors列表中的下一個攔截器
    Interceptor interceptor = interceptors.get(index);
    //調用下一個攔截器的intercept(Chain)方法,傳入剛纔新建的RealInterceptorChain,返回Response
    Response response = interceptor.intercept(next);
    //...

    return response;
}
複製代碼

proceed()方法中再次新建了一個RealInterceptorChain,傳入了index + 1,而獲取攔截器時是經過index獲取,這樣每次都能獲取到下一個攔截器,而後調用下一個攔截器的intercept(Chain)方法,intercept(Chain)方法中就是攔截器的主要功能實現,裏面會繼續調用傳入的RealInterceptorChain的proceed()方法,這樣又會重複上述邏輯,咱們把攔截器看做一條鏈中的節點,這樣每一個攔截器就經過一個個RealInterceptorChain鏈接起來,造成一條鏈,這就是典型的責任鏈模式,從節點的首部開始把請求傳遞下去,每個攔截器都有機會處理這個請求,這又像是一個遞歸的過程,直到最後一個攔截器器處理完請求後,纔開始逐層返回Resquese,攔截器纔是Okhttp核心功能所在,關於攔截器介紹下篇文章再講,這裏只須要知道每個攔截器都表明了一個功能。

通過對攔截器的簡單介紹後,咱們知道最後一個添加的攔截器纔是把請求發送出去而且返回響應的地方,咱們看getResponseWithInterceptorChain()方法,最後一個攔截器的添加是CallServerInterceptor,因此咱們直接看CallServerInterceptor的intercept(Chain)方法實現,以下:

//CallServerInterceptor.java
@Override
public Response intercept(Chain chain) throws IOException {
    //強轉成RealInterceptorChain
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    //獲取Exchange
    Exchange exchange = realChain.exchange();
    //獲取Request
    Request request = realChain.request();

	//一、經過Exchange的writeRequestHeaders(request)方法發送Request的header
    exchange.writeRequestHeaders(request);

    boolean responseHeadersStarted = false;
    Response.Builder responseBuilder = null;
    //由於前面已經講了,默認是GET請求,而GET請求是沒有body的,因此不會進入if分支
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      //省略的是發送Request的body過程
      //...
    } else {
      exchange.noRequestBody();
    }

    //GET請求body爲空,進入這個分支,完成請求
    if (request.body() == null || !request.body().isDuplex()) {
      exchange.finishRequest();
    }

    //省略的是一些監聽回調
    //...
    
    //下面開始獲取網絡請求返回的響應
    
    //二、經過Exchange的readResponseHeaders(boolean)方法獲取響應的header
    if (responseBuilder == null) {
        responseBuilder = exchange.readResponseHeaders(false);
    }
    
    //獲取響應後,經過Builder模式構造Response
    Response response = responseBuilder
        .request(request)
        .handshake(exchange.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    //省略的是response對狀態碼code的處理
    //...
    
    //構造Response的body
    if (forWebSocket && code == 101) {
        //構造一個空的body的Response
        response = response.newBuilder()
            .body(Util.EMPTY_RESPONSE)
            .build();
    } else {
        //經過Exchange的openResponseBody(Response)方法獲取響應的body,而後經過響應的body繼續構造Response
        response = response.newBuilder()
            .body(exchange.openResponseBody(response))
            .build();
    }
    
    //...

    //返回響應Response
    return response;
  }
複製代碼

intercept(Chain)方法中主要作的就是發送請求,獲取響應的事情,註釋中已經寫的很清楚了,發送請求要把header和body分開發送,而獲取響應時也要分別獲取header和body,而發送請求和獲取響應兩個過程都是經過一個Exchange對象進行的,Exchange是在構造RealInterceptorChain時就做爲構造參數傳進RealInterceptorChain中,一直都爲null,直到在ConnectInterceptor的intercept()中才經過Transmitter的newExchange()被賦值,而ConnectInterceptor的下一個攔截器就是CallServerInterceptor,因此CallServerInterceptor能夠經過Chain獲取到Exchange實例,這裏不用細究這個賦值過程,Exchange它主要是用來負責完成一次網絡請求和響應的過程。

這裏我以intercept(Chain)方法中註釋1和註釋2請求header的發送(wirte)和獲取(read)爲例瞭解Exchange的工做過程,首先看Exchange的writeRequestHeaders(Request)方法,以下:

//Exchange.java
public void writeRequestHeaders(Request request) throws IOException {
    try {
        //主要是調用了codec的writeRequestHeaders(request)
        codec.writeRequestHeaders(request);
        //...
    } catch (IOException e) {
        //...
    }
}
複製代碼

咱們再看Exchange的readResponseHeaders(boolean)方法,以下:

//Exchange.java
public @Nullable Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
    try {
      //主要是調用了codec的readResponseHeaders(boolean)
      Response.Builder result = codec.readResponseHeaders(expectContinue);
      //...
      return result;
    } catch (IOException e) {
     //...
    }
  }
複製代碼

從Exchange的兩個方法能夠看出,它把 wirt和read header的任務都交給了codec,codec是什麼呢?codec是ExchangeCodec類型,它是一個接口,它主要用來編碼http請求並解碼http返回結果,因此Exchange中真正幹活的是ExchangeCodec,它的有兩個實現類,分別是Http2ExchangeCodec和Http1ExchangeCodec,分別對應Http2.x和Http1.x,這裏咱們以Http1ExchangeCodec爲例,查看它的writeRequestHeaders(request)和readResponseHeaders(boolean)方法,首先看Http1ExchangeCodec的writeRequestHeaders(request)方法,以下:

//Http1ExchangeCodec.java
@Override 
public void writeRequestHeaders(Request request) throws IOException {
    String requestLine = RequestLine.get(
        request, realConnection.route().proxy().type());
    //調用了writeRequest()
    writeRequest(request.headers(), requestLine);
  }

 public void writeRequest(Headers headers, String requestLine) throws IOException {
    if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
    //能夠看到經過sink把請求頭寫入IO流,發送到服務器,sink是BufferedSink類型
    sink.writeUtf8(requestLine).writeUtf8("\r\n");
    for (int i = 0, size = headers.size(); i < size; i++) {
      sink.writeUtf8(headers.name(i))
          .writeUtf8(": ")
          .writeUtf8(headers.value(i))
          .writeUtf8("\r\n");
    }
    sink.writeUtf8("\r\n");
    state = STATE_OPEN_REQUEST_BODY;
  }
複製代碼

咱們再看Http1ExchangeCodec的readResponseHeaders(boolean)方法,以下:

//Http1ExchangeCodec.java
@Override
public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
   //...
    try {
        StatusLine statusLine = StatusLine.parse(readHeaderLine());
        Response.Builder responseBuilder = new Response.Builder()
            .protocol(statusLine.protocol)
            .code(statusLine.code)
            .message(statusLine.message)
            .headers(readHeaders());//調用了readHeaders()
        //...
        return responseBuilder;
    } catch (EOFException e) {
        //...
    }
}

 private Headers readHeaders() throws IOException {
    Headers.Builder headers = new Headers.Builder();
    //調用了readHeaderLine(),一行一行的讀取header
    for (String line; (line = readHeaderLine()).length() != 0; ) {
      Internal.instance.addLenient(headers, line);
    }
    return headers.build();
  }

 private String readHeaderLine() throws IOException {
     //服務器響應返回,經過source從IO讀取響應頭,source是BufferedSource類型
    String line = source.readUtf8LineStrict(headerLimit);
    headerLimit -= line.length();
    return line;
  }

複製代碼

從Http1ExchangeCodec的兩個方法能夠看出,底層是經過BufferedSink把信息寫入IO流,經過BufferedSource從IO流讀取信息,BufferedSink和BufferedSource都是來自okio這個開源庫的,okhttp底層是經過okio來向網絡中寫入和讀取IO的,想要了解更多可自行查看okio源碼(okio也是square公司的產品)。

到此RealCall的 getResponseWithInterceptorChain()分析完,getResponseWithInterceptorChain()返回Response後,RealCall的execute() 方法就return了,咱們就能夠經過返回的Response獲取咱們想要的信息,但RealCall的execute() 方法就return後,還要繼續執行finally 分支中的邏輯。

三、Dispatcher :: finished(RealCall)

咱們繼續看RealCall的execute()方法的註釋3,調用Dispatcher的finished(AsyncCall)方法,以下:

//Dispatcher.java
void finished(RealCall call) {
    //傳進了runningSyncCalls隊列
    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;
    }

    //緊接着調用promoteAndExecute()方法進行異步任務的調度,若是沒有異步任務要進行,promoteAndExecute()返回false
    boolean isRunning = promoteAndExecute();

    //isRunning等於false且設置了idleCallback,會執行一遍idle任務
    if (!isRunning && idleCallback != null) {
        idleCallback.run();
    }
}
複製代碼

finished()方法中首先嚐試從runningSyncCalls隊列把剛纔經過 executed()入隊的同步任務RealCall移除,若是移除失敗,就拋出異常,若是移除成功,就緊接着調用promoteAndExecute()方法進行異步任務的調度並嘗試執行一遍idle任務,promoteAndExecute()方法在異步請求中再詳細介紹。

小結

至此okhttp的同步請求過程分析完畢,這裏總結一下:當咱們調用call.execute()時,就會發起一個同步請求,而call的實現類是RealCall,因此實際執行的是realCall.execute(),realCall.execute()中執行Dispatcher的executed(RealCall)把這個同步請求任務保存進runningSyncCalls隊列中,而後RealCall執行getResponseWithInterceptorChain()處理同步請求,請求通過層層攔截器後到達最後一個攔截器CallServerInterceptor,在這個攔截器中經過Exchange把請求發送到服務器,而後一樣的經過Exchange得到服務器的響應,根據響應構造Response,而後返回,最後RealCall執行Dispatcher的finished(RealCall)把以前暫時保存的同步請求任務從runningSyncCalls隊列中移除。

下面是同步請求過程的調用鏈:

異步請求 - RealCall.enqueue(Callback)

//RealCall.java
@Override
public void enqueue(Callback responseCallback) {
    //...
    //一、調用Dispatcher的enqueue(AsyncCall)方法
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
複製代碼

異步請求執行的是RealCall的enqueue(Callback)方法,它比同步請求只是多了一個Callback,在Callback的 onResponse(Call, Response)回調中咱們能夠拿到網絡響應返回的Response,RealCall的enqueue(Callback)方法中首先把Callback用AsyncCall包裝起來,而後調用調用Dispatcher的enqueue(AsyncCall)方法。

一、Dispatcher :: enqueue(AsyncCall)

咱們看Dispatcher的enqueue(AsyncCall)方法,以下:

//Dispatcher.java
void enqueue(AsyncCall call) {
    synchronized (this) {
       readyAsyncCalls.add(call);
       //...
    }
    promoteAndExecute();
}

複製代碼

該方法首先把異步請求任務AsyncCall放入readyAsyncCalls隊列,而後調用promoteAndExecute()進行異步任務的調度,咱們看一下Dispatcher 是如何進行異步任務的調度的。

二、Dispatcher :: promoteAndExecute()

promoteAndExecute()方法以下:

//Dispatcher.java
private boolean promoteAndExecute() {
    //準備一個正在執行任務列表executableCalls
    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
        //一、這個for循環主要把readyAsyncCalls中等待執行的異步任務轉移到runningAsyncCalls隊列和executableCalls列表中去
        for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {

            //取出readyAsyncCalls中等待執行的異步任務
            AsyncCall asyncCall = i.next();

            //判斷條件:一、正在運行的異步請求任務不能大於maxRequests;二、等待執行的異步任務的主機請求數不能大於maxRequestsPerHost
            if (runningAsyncCalls.size() >= maxRequests) break; 
            if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue;
            //知足條件,進入下面邏輯

            //把這個等待執行的異步任務從readyAsyncCalls中移除
            i.remove();
            asyncCall.callsPerHost().incrementAndGet();
            
            //把這個等待執行的異步任務添加進executableCalls列表
            executableCalls.add(asyncCall);
            //把這個等待執行的異步任務添加進runningAsyncCalls隊列
            runningAsyncCalls.add(asyncCall);
        }
        
        //runningCallsCount()裏面的邏輯: return runningAsyncCalls.size() + runningSyncCalls.size();
        isRunning = runningCallsCount() > 0;
    }
    //二、這個for循環主要是執行executableCalls列表中的異步任務
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
        AsyncCall asyncCall = executableCalls.get(i);
        //傳進executorService,調用AsyncCall的executeOn()方法,由線程池執行這個異步任務
        asyncCall.executeOn(executorService());
    }

    return isRunning;
}
複製代碼

promoteAndExecute()方法中主要是2個for循環,註釋1的第一個for循環是把符合條件的異步請求任務從readyAsyncCalls轉移(提高)到runningAsyncCalls隊列和添加到executableCalls列表中去,緊接着註釋2的第二個for循環就是遍歷executableCalls列表,從executableCalls列表中獲取AsyncCall對象,而且調用它的executeOn()方法,executeOn()方法傳進了一個Dispatcher的executorService,因此咱們看AsyncCall的executeOn()方法,裏面是真正執行異步請求任務的地方。

2.一、AsyncCall :: executeOn(ExecutorService)

AsyncCall的executeOn()方法以下:

//AsyncCall.java
void executeOn(ExecutorService executorService) {
    boolean success = false;
    try {
        //傳進this,執行AsyncCall異步任務,AsyncCall本質是Runnable
        executorService.execute(this);
        success = true;
    } catch (RejectedExecutionException e) {
       //...
    } finally {
        if (!success) {
            //異步任務執行失敗,調用Dispatcher的finished(AsyncCall)方法
            client.dispatcher().finished(this); 
        }
    }
    
複製代碼

能夠看到,裏面的主要邏輯就是調用 executorService.execute(this)執行當前的AsyncCall異步任務,前面已經說過AsyncCall實現了NamedRunnable,本質是Runnable,以下:

final class AsyncCall extends NamedRunnable {
    //...
}

public abstract class NamedRunnable implements Runnable {
	//... 
  @Override
    public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      //run方法中執行execute()方法
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

複製代碼

線程池執行到此異步任務時,它的run方法就會被執行,而run方法主要調用execute()方法,而execute()方法是一個抽象方法,AsyncCall實現了NamedRunnable,因此AsyncCall重寫了execute()實現了執行邏輯,因此咱們直接看AsyncCal的execute()方法。

2.二、AsyncCal :: execute()

AsyncCal的execute()方法以下:

//AsyncCall.java
@Override 
protected void execute() {
    //...
    try {
        //調用RealCall的getResponseWithInterceptorChain()方法處理請求
        Response response = getResponseWithInterceptorChain();
        signalledCallback = true;
        //請求處理完畢,返回響應,回調Callback的onResponse()方法
        responseCallback.onResponse(RealCall.this, response);
    } catch (IOException e) {
        //...
    } finally {
        //異步請求任務執行完畢,調用Dispatcher的finished(AsyncCall)方法
        client.dispatcher().finished(this);
    }
}
複製代碼

AsyncCal的execute()方法的邏輯和前面介紹的同步請求過程異曲同工,首先調用RealCall的getResponseWithInterceptorChain()方法處理請求,請求處理完畢後,返回響應Response,這時回調咱們調用Call.enqueue(Callback)時傳進來的Callback的onResponse()方法,最後在finally語句中調用Dispatcher的finished(AsyncCall)方法來把異步請求任務從runningAsyncCalls隊列中移除出去,這個移除邏輯和上面同步請求任務的移除邏輯同樣,只是此次是從runningAsyncCalls移除而不是runningSyncCalls,以下:

//Dispatcher.java
void finished(AsyncCal call) {
    //傳進runningAsyncCalls,而不是runningSyncCalls
    finished(runningSyncCalls, call);
}
複製代碼

小結

至此okhttp的異步請求過程分析完畢,這裏再次總結一下,當咱們調用call.enqueue(Callback)時,就會發起一個異步請求,實際執行的是realCall.enqueue(Callback),它比同步請求只是多了一個Callback參數,而後realCall.execute()中先把傳進來的Callback包裝成一個AsyncCall,而後執行Dispatcher的enqueue(AsyncCall)把這個異步請求任務保存進readyAsyncCalls隊列中,保存後開始執行 promoteAndExecute()進行異步任務的調度,它會先把符合條件的異步請求任務從readyAsyncCalls轉移到runningAsyncCalls隊列和添加到executableCalls列表中去,而後遍歷executableCalls列表,逐個執行AsyncCall 的executeOn(ExecutorService),而後在這個方法中AsyncCall會把本身放進Dispatcher 的線程池,等待線程池的調度,當線程池執行到這個AsyncCall時,它的run方法就會被執行,從而執行重寫的execute()方法,execute()方法中的流程和同步請求流程大體相同。

下面是異步請求過程的調用鏈:

結語

okhttp經過Builder模式建立OkHttpClient、Request和Response,經過client.newCall(Resquest)建立一個Call,用於發起異步或同步請求,請求會通過Dispatcher、一系列攔截器,最後經過okio與服務器創建鏈接、發送數據並解析返回結果,這個過程如圖:

以上就是對okhttp的請求流程的分析,若有錯誤,歡迎指出。

參考文章:

OkHttp 3.x 源碼解析之Dispather分發器

相關文章
相關標籤/搜索