OkHttp3 之旅

一 為什么推薦使用Okhttp3?

首先,我并不覺得OkHttp是一個(gè)網(wǎng)絡(luò)框架。okhttp對(duì)標(biāo)的,應(yīng)該是HttpClient或者HttpURLConnection,okhttp應(yīng)該是一種新的網(wǎng)絡(luò)請(qǐng)求方法,而網(wǎng)絡(luò)框架,應(yīng)該是基于上面幾個(gè)網(wǎng)絡(luò)訪問方式進(jìn)行封裝的。像volley(基于httpClient和httpURLConnection)或者retrofit2(基于OkHttp3)。
好吧,扯遠(yuǎn)了~~
我們?yōu)槭裁匆褂肙kHttp3作為我們新的網(wǎng)絡(luò)請(qǐng)求方式?我認(rèn)為有以下幾點(diǎn):

1 OkHttp支持SPDY、http2.0和https

2 OkHttp3內(nèi)置ConnectionPool連接池,可以實(shí)現(xiàn)多路復(fù)用(在spdy不可用的情況下使用)

3 利用GZip壓縮內(nèi)容

4 具備超時(shí)重連機(jī)制,調(diào)用方不用自己去進(jìn)行自定義連接動(dòng)作

5 具備緩存機(jī)制,可以避免重復(fù)的網(wǎng)絡(luò)請(qǐng)求

6 使用簡(jiǎn)單,封裝的很好,方便調(diào)用(這個(gè)吧,不知道算不算,哈哈哈)

二 OKHttp3.0集成

集成方式還是比較簡(jiǎn)單的,我們只需要在我們的app的build.gradle中添加依賴

compile 'com.squareup.okhttp3:okhttp:3.10.0'

同步調(diào)用:

private void sendRequestSync() throws IOException {
    OkHttpClient mHttpClient=new OkHttpClient();
    Request request= new Request.Builder().url(ENDPOINT).build();
    mHttpClient.newCall(request).execute();
}

異步調(diào)用:

 private void sendRequestAsyn(){
    OkHttpClient mHttpClient=new OkHttpClient();
    Request request= new Request.Builder().url(ENDPOINT).build();
    mHttpClient.newCall(request).enqueue(new Callback() {
        @Override 
        public void onFailure(Call call, IOException e) {
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            String json=response.body().string();
        }
    });
}

三 OkHttp3請(qǐng)求流程分析

由于同步執(zhí)行的代碼比較簡(jiǎn)單直觀,我們這里用異步請(qǐng)求的方式進(jìn)行分析。首先我們把整個(gè)網(wǎng)絡(luò)請(qǐng)求分為4個(gè)部分

3.1 OkHttpClient的初始化

 OkHttpClient mHttpClient=new OkHttpClient();   

OKHttpClient其實(shí)是call的一個(gè)工廠類,OkHttpClient是用來發(fā)起http請(qǐng)求,并拿到http的response的處理類。因?yàn)槊恳粋€(gè)HttpClient都維護(hù)有自己的連接池和線程池,所以不建議在每一次請(qǐng)求的時(shí)候都去初始化一個(gè)OkHttpcliet,而是在我們的項(xiàng)目中,只維護(hù)一個(gè)單例類就可以。
我們有兩種方法去初始化一個(gè)OkHttpClient:

方法一:直接new OkHttpClient(),創(chuàng)建一個(gè)默認(rèn)的OkHttpClient;

方法二:通過builder去創(chuàng)建:

public final OkHttpClient client = new OkHttpClient.Builder()

.addInterceptor(new HttpLoggingInterceptor())

.cache(new Cache(cacheDir, cacheSize))

.build();  

3.2 異步方法執(zhí)行

mHttpClient.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            String json=response.body().string();
        }
    });

在這里,我們首先去查看newcall(request)方法

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

所以,我們知道,最終的enqueue方法是在realCall類中調(diào)用的。我們前去查看源碼

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

從這段代碼的最后一個(gè)client.dispatcher(),好了到這里,我們就需要去看看Dispatcher類了。

3.3:Dispatcher類

Dispatcher是OkHttp的異步請(qǐng)求策略類,在這里,OkHttp3定義了多個(gè)重要的參數(shù),分別是

public final class Dispatcher {
    //最大的請(qǐng)求數(shù)量
    private int maxRequests = 64;
    
    //每一個(gè)host的最大請(qǐng)求數(shù)
    private int maxRequestsPerHost = 5;

    private @Nullable Runnable idleCallback; 
    private @Nullable ExecutorService executorService;

    //當(dāng)前準(zhǔn)備執(zhí)行的隊(duì)列
    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

    //當(dāng)前正在執(zhí)行的隊(duì)列,包括那些尚未結(jié)束的請(qǐng)求
    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

    //同步執(zhí)行的隊(duì)列,包括還沒有結(jié)束的請(qǐng)求
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

    public Dispatcher(ExecutorService executorService) {
        this.executorService = executorService;
     }

    public Dispatcher() {
     }
}

dispatch中查看enqueue的處理方式

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

也就是,當(dāng)前的運(yùn)行隊(duì)列小于最大值maxRequest并且當(dāng)前運(yùn)行的每個(gè)host的請(qǐng)求小于host最大請(qǐng)求數(shù)的時(shí)候,我們就把當(dāng)前的call加入到執(zhí)行隊(duì)列中,否則就加入到等待隊(duì)列中,等待okHttp執(zhí)行。那么,我們是在哪里執(zhí)行這些隊(duì)列中的請(qǐng)求的呢?答案是RealCall

3.4:RealCall

RealCall里面的內(nèi)部類AsyncCall封裝了異步執(zhí)行下的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 {
      eventListener.callFailed(RealCall.this, e);
      responseCallback.onFailure(RealCall.this, e);
    }
  } finally {
    client.dispatcher().finished(this);
  }
}

這段代碼,我們只關(guān)注兩個(gè)核心的方法,一個(gè)是getResponseWithInterceptorChain(),另一個(gè)是client.dispatcher().finished(this);

3.4.1:getResponseWithInterceptorChain()源碼如下:

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, this, eventListener, client.connectTimeoutMillis(),
    client.readTimeoutMillis(), client.writeTimeoutMillis());

return chain.proceed(originalRequest);
}

我們從execute()方法可以知道,我們真正發(fā)出網(wǎng)絡(luò)請(qǐng)求,獲取response方法就是在這個(gè)方法中。我們一步一步分析:

第一:多攔截器的構(gòu)造。

getResponseWithInterceptorChain()方法首先定義了一個(gè)攔截器list,從代碼中我們可以看到,添加的順序分別為:

1 移動(dòng)端自定義的攔截器:client.interceptors()(為什么自定義的攔截器要第一個(gè)添加,后面有介紹)

2 retryAndFollowUpInterceptor 失敗重試的攔截器

3 BridgeInterceptor:官方解釋,這是一個(gè)連接我們應(yīng)用和網(wǎng)絡(luò)的橋梁。首先通過用戶請(qǐng)求創(chuàng)建一個(gè)網(wǎng)咯請(qǐng)求,接著把這個(gè)請(qǐng)求發(fā)出處理,最后把網(wǎng)絡(luò)返回的值轉(zhuǎn)換成用戶所需要的值

4 CacheInterceptor:緩存攔截器

5 ConnectInterceptor:網(wǎng)絡(luò)請(qǐng)求攔截器。用來跟服務(wù)端進(jìn)行連接

6 CallServerInterceptor:這個(gè)是我們網(wǎng)絡(luò)請(qǐng)求的鏈?zhǔn)秸{(diào)用的最后一個(gè)攔截器,用來將數(shù)據(jù)丟到網(wǎng)絡(luò)中進(jìn)行處理

第二:RealInterceptorChain 初始化,鏈?zhǔn)秸{(diào)用正式開始

RealInterceptorChain類我們主要看process()方法。

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
  RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();

calls++;

// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
  throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
      + " must retain the same host and port");
}

// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpCodec != null && calls > 1) {
  throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
      + " must call proceed() exactly once");
}

// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
    connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
    writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);

// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
  throw new IllegalStateException("network interceptor " + interceptor
      + " must call proceed() exactly once");
}

// Confirm that the intercepted response isn't null.
if (response == null) {
  throw new NullPointerException("interceptor " + interceptor + " returned null");
}

if (response.body() == null) {
  throw new IllegalStateException(
      "interceptor " + interceptor + " returned a response with no body");
}

return response;
}  

前面的異常處理邏輯我們一律不看。我們主要看這一段

 // Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
    connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
    writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);  

我們?cè)谇癛ealCall中AsynCall的execute()方法中,傳入RealInterceptorChain初始化的時(shí)候,index是0的,也就是說,上面的interceptors.get(index);其實(shí)等于interceptors.get(0)=client.interceptors(),也即是我們移動(dòng)端自定義的攔截器。最后通過

Response response = interceptor.intercept(next);

我們可以看到,最后回到的,就是各個(gè)自定義攔截器中的intercept()方法中(各位如果不理解,可以去看看官方demo里面HttpLoggingInterceptor.java的實(shí)現(xiàn))。

if (retryAndFollowUpInterceptor.isCanceled()) {
      signalledCallback = true;
      responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
    } else {
      signalledCallback = true;
      responseCallback.onResponse(RealCall.this, response);
    }  

這段代碼就是為了回調(diào)前面我們調(diào)用enqueue的時(shí)候的listener,返回當(dāng)前的網(wǎng)絡(luò)請(qǐng)求是成功的,還是失敗的。這個(gè)簡(jiǎn)單,暫且不做分析。

3.4.3:client.dispatcher().finished(this);

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

我們可以看到,無論是同步還是異步執(zhí)行網(wǎng)絡(luò)請(qǐng)求,最終都會(huì)調(diào)用到這個(gè)finish方法中。這個(gè)就是OkHttp維護(hù)當(dāng)前連接池的方法,每當(dāng)一個(gè)請(qǐng)求結(jié)束后,都會(huì)把當(dāng)前的call從當(dāng)前的運(yùn)行隊(duì)列中移除。然后再執(zhí)行promoteCalls(),把等待運(yùn)行隊(duì)列中的下一個(gè)請(qǐng)求放入運(yùn)行隊(duì)列中。

四 總結(jié)

自此,OkHttp3.0的執(zhí)行流程分析到此結(jié)束。但是OkHttp的內(nèi)容遠(yuǎn)不止這么復(fù)雜,接下來我會(huì)在下一篇文章,對(duì)OkHttp的其他技術(shù)細(xì)節(jié)結(jié)合源碼進(jìn)行詳細(xì)分析。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 參考資源 官網(wǎng) 國(guó)內(nèi)博客 GitHub官網(wǎng) 鑒于一些關(guān)于OKHttp3源碼的解析文檔過于碎片化,本文系統(tǒng)的,由淺入...
    風(fēng)骨依存閱讀 12,710評(píng)論 11 82
  • 在《打車APP實(shí)戰(zhàn)》課程中,我們使用 OkHttp3 簡(jiǎn)單搭建了一個(gè)網(wǎng)絡(luò)框架, 實(shí)踐了 OkHttp3 的用法。不...
    大利貓閱讀 1,929評(píng)論 0 25
  • 這篇文章主要講 Android 網(wǎng)絡(luò)請(qǐng)求時(shí)所使用到的各個(gè)請(qǐng)求庫的關(guān)系,以及 OkHttp3 的介紹。(如理解有誤,...
    小莊bb閱讀 1,331評(píng)論 0 4
  • OkHttp源碼的samples的簡(jiǎn)單使用的示例: public static void main(String....
    _warren閱讀 878評(píng)論 0 1
  • 首先要拿到圖片的網(wǎng)絡(luò)地址(本地圖片就沒什么意思了),然后根據(jù)下面這個(gè)方法獲取圖片的緩存地址: 記住,這個(gè)方法要在子...
    24K純帥豆閱讀 18,715評(píng)論 5 13

友情鏈接更多精彩內(nèi)容