OkHttp之網絡請求流程(一)

本篇文章主要介紹OkHttp從發起請求到接收到結果的整個執行流程. 由上篇的概述可知,OkHttp支持同步和異步的請求,這里我們以異步請求為例進行分析

一,異步OkHttp請求示例

private void testOkHttp() throws IOException {
        final OkHttpClient client = new OkHttpClient();
        final Request request = new Request.Builder().url("https://www.google.com.hk").build();

        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                    Log.i(TAG,response.toString());
                    Log.i(TAG,response.body().string());
            }
        });
    }

上面的代碼就實現了一個網絡請求. 創建一個OkHttpClient對象,他包含了分發器,連接池,攔截器等等內容. 接下來創建一個請求對象,通過OkHttpClient的newCall()方法對其包裝,然后添加請求隊列,然后回調結果

Tip
對于OkHttpClient封裝一個單實例,讓所有的Http請求都重用它, OkHttp將會有更好的性能.因為每一個OkHttpClient實例都持有自己的連接池,線程池等,重用連接池和線程池能夠減少網絡訪問延遲以及節省內存,減少資源的浪費

下面我們就詳細的分析一下OkHttp詳細的網絡訪問流程

二,OkHttp詳訪流程

1,創建HttpClient對象.

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

  OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    this.proxySelector = builder.proxySelector;
    this.cookieJar = builder.cookieJar;
    this.cache = builder.cache;
    this.internalCache = builder.internalCache;
    this.socketFactory = builder.socketFactory;

    ... ...
    this.proxyAuthenticator = builder.proxyAuthenticator;
    this.authenticator = builder.authenticator;
    this.connectionPool = builder.connectionPool;
    this.dns = builder.dns;
    this.followSslRedirects = builder.followSslRedirects;
    this.followRedirects = builder.followRedirects;
    this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;
    this.pingInterval = builder.pingInterval;
  }

由上面可知HttpClient創建的時候,初始化了連接池,線程池,默認攔截器等等,為接下來的網絡請求鋪墊

2,創建Request對象

Request request = new Request.Builder().url("https://www.google.com.hk").build();

通過Request中的Builder構建一個請求對象
這個Request對象我們可以理解成一個請求Bean,里面封裝了請求的url, 請求方法(post get put delete等),請求頭,請求體,以及tag(用于批量取消)

 public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
  }

接下來繼續對Request對象進行包裝,使其能夠執行execute,入隊enqueue, 取消cancel, 判斷狀態 isCancel和isExecuted
上面的call指向的是RealCall對象
我們看下Call接口

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

3,將請求入隊

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

OkHttpClient的dispatcher()方法返回的是一個Dispatcher對象,每個Dispatcher使用線程池來運行上面的Call任務.

接下來我們看下Dispacher的入隊操作

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

runningAsyncCalls存放所有正在執行的異步任務
readyAsyncCalls存放所有正在等待執行的異步任務
當正在執行的請求小于最大請求數64且每個主機的最大請求數不超過5 則直接加入到正在執行的異步隊列中且執行.
否則加入到正在等待執行的異步隊列中等待執行.

**由上我們可知
請求相同主機的最大請求數目是5
所有請求最大數目是64
實際開發中,用戶量一般的app服務器只有一個,那么一般最大的請求就是5
**
好了,一個請求封裝準備了這么多, 順利入隊,下面我們看看他下面是怎么執行的

4,執行
RealCall封裝了原始的Request,在RealCall中有一個非靜態內部類AsyncCall,AsyncCall中的execute方法就是任務要執行的代碼

final class AsyncCall extends NamedRunnable {
   ... ...
   ... ...
    @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對象,下面我們看下他的攔截鏈

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

RealInterceptorChain.java
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      Connection connection) throws IOException {
   ... ...
    //取出下一個攔截器,遞歸調用
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
  ... ...
    return response;
  }

1)首先添加用戶自定義的攔截器(如果有),接著依次添加重試攔截器,橋接攔截器,緩存攔截器,連接攔截器以及訪問服務器的攔截器.
這些攔截器的添加順序是固定的,不能改變
2)這些攔截器形成一個攔截鏈,每個攔截器專司其自己的職責, 將處理好的請求交給下一個攔截器,最后一個攔截器處理完畢后,再將其交給上一個攔截器,直到第一個, 最后將結果返回.這樣一個責任鏈就形成了
3)每個攔截器都實現Interceptor接口

public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    Connection connection();
  }
}

4)對于攔截器之間的調用過程可能不太容易理解,下面針對2)給出一個示意圖
Req: 請求Request縮寫
Res: 響應Response縮寫

基本流程圖.png

5,結果回調
我們再將目光回到AsyncCall類


    @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()一系列的處理得到結果后.
1),會首先判斷結果是否取消, 如果取消則回調傳入的回調onFail中
否則,回調結果到onResponse中
2),接下來是掃尾操作
調用Dispatcher的finish方法

void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

  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();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

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

它主要做了兩件事兒
A,將剛才已經完成的任務從runningAsyncCalls移除
B,如果runningAsyncCall小于5(每個主機的最大訪問數量), 則從readyAsyncCalls移除添加到runningAsyncCalls中,并且執行

至此一個完整的OkHttp異步請求完畢
總的來說: 就是根據請求的url, 請求方法, 請求體構造一個Request的Bean, 然后將Request封裝成一個RealCall,RealCall實現了Call接口,他有對執行邏輯的操作(取消,執行,判斷執行的狀態等), 接下來通過全局的OkHttpClient(擁有線程池,連接池,分發器等資源),利用分發器Dispatcher加入到執行隊列或者等待隊列中.
然后線程池執行RealCall的非靜態內部類AsyncCall的execute方法, 然后通過getResponseWithInterceptorChain來進行一系列的攔截器操作處理,回調請求結果.
最后進行掃尾操作,移除正在執行隊列的當前請求,將等待隊列的請求添加到執行隊列.

OkHttp的處理核心是在攔截器,每個攔截器都有自己的職責,下一篇文章詳細分析各個攔截器具體都干了什么
OkHttp之攔截器(二)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 關于okhttp是一款優秀的網絡請求框架,關于它的源碼分析文章有很多,這里分享我在學習過程中讀到的感覺比較好的文章...
    蕉下孤客閱讀 3,621評論 2 38
  • 這篇文章主要講 Android 網絡請求時所使用到的各個請求庫的關系,以及 OkHttp3 的介紹。(如理解有誤,...
    小莊bb閱讀 1,213評論 0 4
  • OkHttp解析系列 OkHttp解析(一)從用法看清原理OkHttp解析(二)網絡連接OkHttp解析(三)關于...
    Hohohong閱讀 21,014評論 4 58
  • OKHTTP異步和同步請求簡單分析OKHTTP攔截器緩存策略CacheInterceptor的簡單分析OKHTTP...
    未見哥哥閱讀 35,444評論 6 27
  • 母親總是很自然的將母親節我們送給她的花,獻在奶奶的照片前,然后撒上水。說起奶奶,有很多話想說,又不知從何說起...
    口口王木木閱讀 381評論 0 0