okhttp 源碼請求流程記錄

記錄以okhttp 版本號為 3.14.9 的請求為例坚踩,看源碼執(zhí)行一個請求的基本流程

開始使用
        String url = "http://wwww.baidu.com";
        final Request request = new Request.Builder()
                .url(url)
                .build();
        OkHttpClient okHttpClient=new OkHttpClient();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d(getClass().getSimpleName(), "onFailure: ");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.d(getClass().getSimpleName(), "onResponse: " + response.code());
            }
        });

從 call.enqueue 開始 ctrl +鼠標(biāo)左擊可以看到Call是個接口,主要執(zhí)行類為RealCall類的enqueue方法槽唾,

    public void enqueue(Callback responseCallback) {
        synchronized(this) {
            if (this.executed) {
                throw new IllegalStateException("Already Executed");
            }

            this.executed = true;
        }

        this.transmitter.callStart();
        this.client.dispatcher().enqueue(new RealCall.AsyncCall(responseCallback));
    }

根據(jù)上面代碼可以看到 enqueue函數(shù)里new RealCall里的內(nèi)部類AsyncCall對象,而AsyncCall是一個繼承了NamedRunnable類伐谈,NamedRunnable實現(xiàn)了runable 接口的實現(xiàn)類医吊,并且NamedRunnable類的run方法調(diào)用execute()方法焕议,如下代碼所示

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

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

    public final void run() {
        String oldName = Thread.currentThread().getName();
        Thread.currentThread().setName(this.name);

        try {
            this.execute();
        } finally {
            Thread.currentThread().setName(oldName);
        }

    }

    protected abstract void execute();
}

再看看AsyncCall類的execute()方法挠日,可以從下面看到請求的結(jié)果從getResponseWithInterceptorChain方法中獲取到疮绷。

protected void execute() {
            boolean signalledCallback = false;
            RealCall.this.transmitter.timeoutEnter();

            try {
              //請求結(jié)束獲取到請求結(jié)果
                Response response = RealCall.this.getResponseWithInterceptorChain();
                signalledCallback = true;
                this.responseCallback.onResponse(RealCall.this, response);
            } catch (IOException var8) {
                if (signalledCallback) {
                    Platform.get().log(4, "Callback failure for " + RealCall.this.toLoggableString(), var8);
                } else {
                    this.responseCallback.onFailure(RealCall.this, var8);
                }
            } catch (Throwable var9) {
                RealCall.this.cancel();
                if (!signalledCallback) {
                    IOException canceledException = new IOException("canceled due to " + var9);
                    canceledException.addSuppressed(var9);
                    this.responseCallback.onFailure(RealCall.this, canceledException);
                }

                throw var9;
            } finally {
                RealCall.this.client.dispatcher().finished(this);
            }

        }

從下面代碼可以看到getResponseWithInterceptorChain方法主要就是添加多個攔截器,并放入攔截器數(shù)組中嚣潜。
下一步就是創(chuàng)建個攔截鏈類RealInterceptorChain 冬骚,并調(diào)用攔截鏈的proceed方法,

  Response getResponseWithInterceptorChain() throws IOException {
        List<Interceptor> interceptors = new ArrayList();
        interceptors.addAll(this.client.interceptors());
        interceptors.add(new RetryAndFollowUpInterceptor(this.client));
        interceptors.add(new BridgeInterceptor(this.client.cookieJar()));
        interceptors.add(new CacheInterceptor(this.client.internalCache()));
        interceptors.add(new ConnectInterceptor(this.client));
        if (!this.forWebSocket) {
            interceptors.addAll(this.client.networkInterceptors());
        }

        interceptors.add(new CallServerInterceptor(this.forWebSocket));
        Chain chain = new RealInterceptorChain(interceptors, this.transmitter, (Exchange)null, 0, this.originalRequest, this, this.client.connectTimeoutMillis(), this.client.readTimeoutMillis(), this.client.writeTimeoutMillis());
        boolean calledNoMoreExchanges = false;

        Response var5;
        try {
            Response response = chain.proceed(this.originalRequest);
            if (this.transmitter.isCanceled()) {
                Util.closeQuietly(response);
                throw new IOException("Canceled");
            }

            var5 = response;
        } catch (IOException var9) {
            calledNoMoreExchanges = true;
            throw this.transmitter.noMoreExchanges(var9);
        } finally {
            if (!calledNoMoreExchanges) {
                this.transmitter.noMoreExchanges((IOException)null);
            }

        }

        return var5;
    }

從下面代碼proceed方法里的操作主要就是把攔截器數(shù)組里的攔截器從第一個攔截器開始懂算,調(diào)用攔截器的intercept方法只冻,傳入的參數(shù)為RealInterceptorChain 類本身的對象。由RealCall類的getResponseWithInterceptorChain方法可知第一個攔截器為RetryAndFollowUpInterceptor 计技。

    public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange) throws IOException {
        if (this.index >= this.interceptors.size()) {
            throw new AssertionError();
        } else {
            ++this.calls;
            if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
                throw new IllegalStateException("network interceptor " + this.interceptors.get(this.index - 1) + " must retain the same host and port");
            } else if (this.exchange != null && this.calls > 1) {
                throw new IllegalStateException("network interceptor " + this.interceptors.get(this.index - 1) + " must call proceed() exactly once");
            } else {
                RealInterceptorChain next = new RealInterceptorChain(this.interceptors, transmitter, exchange, this.index + 1, request, this.call, this.connectTimeout, this.readTimeout, this.writeTimeout);//傳入類本身的對象
                Interceptor interceptor = (Interceptor)this.interceptors.get(this.index);
                Response response = interceptor.intercept(next);
                if (exchange != null && this.index + 1 < this.interceptors.size() && next.calls != 1) {
                    throw new IllegalStateException("network interceptor " + interceptor + " must call proceed() exactly once");
                } else if (response == null) {
                    throw new NullPointerException("interceptor " + interceptor + " returned null");
                } else if (response.body() == null) {
                    throw new IllegalStateException("interceptor " + interceptor + " returned a response with no body");
                } else {
                    return response;
                }
            }
        }
    }

從下面代碼RetryAndFollowUpInterceptor類的intercept方法喜德,做好本身的業(yè)務(wù)處理后,在調(diào)用RealInterceptorChain的proceed方法垮媒,如此流程調(diào)用完攔截器數(shù)組的每一個攔截器類舍悯。 請求服務(wù)器的鏈接攔截器類CallServerInterceptor,有命名名稱可知為鏈接攔截器涣澡,主要進(jìn)行與服務(wù)器之間的請求鏈接

 public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        RealInterceptorChain realChain = (RealInterceptorChain)chain;
        Transmitter transmitter = realChain.transmitter();
        int followUpCount = 0;
        Response priorResponse = null;

        while(true) {
            transmitter.prepareToConnect(request);
            if (transmitter.isCanceled()) {
                throw new IOException("Canceled");
            }

            boolean success = false;

            Response response;
            try {
            //調(diào)用傳入的Chain類的proceed方法贱呐,即攔截器鏈的對象
                response = realChain.proceed(request, transmitter, (Exchange)null);
                success = true;
            } catch (RouteException var16) {
                if (this.recover(var16.getLastConnectException(), transmitter, false, request)) {
                    continue;
                }

                throw var16.getFirstConnectException();
            } catch (IOException var17) {
                boolean requestSendStarted = !(var17 instanceof ConnectionShutdownException);
                if (!this.recover(var17, transmitter, requestSendStarted, request)) {
                    throw var17;
                }
                continue;
            } finally {
                if (!success) {
                    transmitter.exchangeDoneDueToException();
                }

            }

            if (priorResponse != null) {
                response = response.newBuilder().priorResponse(priorResponse.newBuilder().body((ResponseBody)null).build()).build();
            }

            Exchange exchange = Internal.instance.exchange(response);
            Route route = exchange != null ? exchange.connection().route() : null;
            Request followUp = this.followUpRequest(response, route);
            if (followUp == null) {
                if (exchange != null && exchange.isDuplex()) {
                    transmitter.timeoutEarlyExit();
                }

                return response;
            }

            RequestBody followUpBody = followUp.body();
            if (followUpBody != null && followUpBody.isOneShot()) {
                return response;
            }

            Util.closeQuietly(response.body());
            if (transmitter.hasExchange()) {
                exchange.detachWithViolence();
            }

            ++followUpCount;
            if (followUpCount > 20) {
                throw new ProtocolException("Too many follow-up requests: " + followUpCount);
            }

            request = followUp;
            priorResponse = response;
        }
    }

從下面CallServerInterceptor類的CallServerInterceptor方法中可以看到丧诺,該類主要做一下幾方面處理

  • requestHeader 請求頭處理
  • RequestBody 請求體處理
  • 后臺返回responseHeader響應(yīng)頭處理
  • 后臺返回responseBody響應(yīng)體處理
    public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain)chain;
        Exchange exchange = realChain.exchange();
        Request request = realChain.request();
        long sentRequestMillis = System.currentTimeMillis();
        exchange.writeRequestHeaders(request);
        boolean responseHeadersStarted = false;
        Builder responseBuilder = null;
        if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
            if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
                exchange.flushRequest();
                responseHeadersStarted = true;
                exchange.responseHeadersStart();
                responseBuilder = exchange.readResponseHeaders(true);
            }

            if (responseBuilder == null) {
                BufferedSink bufferedRequestBody;
                if (request.body().isDuplex()) {
                    exchange.flushRequest();
                    bufferedRequestBody = Okio.buffer(exchange.createRequestBody(request, true));
                    request.body().writeTo(bufferedRequestBody);
                } else {
                    bufferedRequestBody = Okio.buffer(exchange.createRequestBody(request, false));
                    request.body().writeTo(bufferedRequestBody);
                    bufferedRequestBody.close();
                }
            } else {
                exchange.noRequestBody();
                if (!exchange.connection().isMultiplexed()) {
                    exchange.noNewExchangesOnConnection();
                }
            }
        } else {
            exchange.noRequestBody();
        }

        if (request.body() == null || !request.body().isDuplex()) {
            exchange.finishRequest();
        }

        if (!responseHeadersStarted) {
            exchange.responseHeadersStart();
        }

        if (responseBuilder == null) {
            responseBuilder = exchange.readResponseHeaders(false);
        }

        Response response = responseBuilder.request(request).handshake(exchange.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();
        int code = response.code();
        if (code == 100) {
            response = exchange.readResponseHeaders(false).request(request).handshake(exchange.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();
            code = response.code();
        }

        exchange.responseHeadersEnd(response);
        if (this.forWebSocket && code == 101) {
            response = response.newBuilder().body(Util.EMPTY_RESPONSE).build();
        } else {
            response = response.newBuilder().body(exchange.openResponseBody(response)).build();
        }

        if ("close".equalsIgnoreCase(response.request().header("Connection")) || "close".equalsIgnoreCase(response.header("Connection"))) {
            exchange.noNewExchangesOnConnection();
        }

        if ((code == 204 || code == 205) && response.body().contentLength() > 0L) {
            throw new ProtocolException("HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
        } else {
            return response;
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末入桂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子驳阎,更是在濱河造成了極大的恐慌抗愁,老刑警劉巖馁蒂,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蜘腌,居然都是意外死亡沫屡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門撮珠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沮脖,“玉大人,你說我怎么就攤上這事芯急∩捉欤” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵娶耍,是天一觀的道長免姿。 經(jīng)常有香客問我,道長榕酒,這世上最難降的妖魔是什么胚膊? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮想鹰,結(jié)果婚禮上紊婉,老公的妹妹穿的比我還像新娘。我一直安慰自己辑舷,他們只是感情好肩榕,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惩妇,像睡著了一般株汉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上歌殃,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天乔妈,我揣著相機與錄音,去河邊找鬼氓皱。 笑死路召,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的波材。 我是一名探鬼主播股淡,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼廷区!你這毒婦竟也來了唯灵?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤隙轻,失蹤者是張志新(化名)和其女友劉穎埠帕,沒想到半個月后垢揩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡敛瓷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年叁巨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呐籽。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡锋勺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出狡蝶,到底是詐尸還是另有隱情宙刘,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布牢酵,位于F島的核電站悬包,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏馍乙。R本人自食惡果不足惜布近,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望丝格。 院中可真熱鬧撑瞧,春花似錦、人聲如沸显蝌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽曼尊。三九已至酬诀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間骆撇,已是汗流浹背瞒御。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留神郊,地道東北人肴裙。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像涌乳,于是被迫代替她去往敵國和親蜻懦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354

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