淺析okhttp和Retrofit(一)

概述

okhttp,一個(gè)處理網(wǎng)絡(luò)請(qǐng)求的開源項(xiàng)目,是安卓端最火熱的輕量級(jí)框架,由移動(dòng)支付Square公司貢獻(xiàn)用于替代HttpUrlConnection和Apache HttpClient,而Retrofit也是Square開源的一款適用于Android網(wǎng)絡(luò)請(qǐng)求的框架。Retrofit底層是基于OkHttp實(shí)現(xiàn)的,與其他網(wǎng)絡(luò)框架不同的是,它更多使用運(yùn)行時(shí)注解的方式提供功能。

現(xiàn)在比較常用的網(wǎng)絡(luò)請(qǐng)求方式大概有四種:Android-Async-Http、Volley、OkHttp、Retrofit,下面借用一張圖來(lái)讓你大概了解全他們的特點(diǎn)和他們之間的區(qū)別

HttpClient簡(jiǎn)介:

HttpClient 是Apache的一個(gè)三方網(wǎng)絡(luò)框架,網(wǎng)絡(luò)請(qǐng)求做了完善的封裝,api眾多,用起來(lái)比較方便,開發(fā)快。實(shí)現(xiàn)比較穩(wěn)定,bug比較少,但是正式由于其api眾多,是我們很難再不破壞兼容性的情況下對(duì)其進(jìn)行擴(kuò)展。所以,Android團(tuán)隊(duì)對(duì)提升和優(yōu)化httpclient積極性并不高。android5.0被廢棄,6.0逐漸刪除。

HttpURLConnection簡(jiǎn)介

HttpURLConnection是一個(gè)多用途、輕量級(jí)的http客戶端。它對(duì)網(wǎng)絡(luò)請(qǐng)求的封裝沒有HttpClient徹底,api比較簡(jiǎn)單,用起來(lái)沒有那么方便。但是正是由于此,使得我們能更容易的擴(kuò)展和優(yōu)化的HttpURLConnection。不過(guò),再android2.2之前一直存在著一些令人煩的bug,比如一個(gè)人可讀的inputstream調(diào)用它的close方法的時(shí)候,會(huì)使得連接池實(shí)效,通常的做法就是禁用連接池。因此,在android2.2之前建議使用穩(wěn)定的HttpClient,android2.2之后使用更容易擴(kuò)展和優(yōu)化的HttpURLConnection。

這篇文章我們先說(shuō)說(shuō)okhttp
OkHttp官網(wǎng)地址:http://square.github.io/okhttp/
OkHttp GitHub地址:https://github.com/square/okhttp

基本使用

配置

導(dǎo)入Jar包
點(diǎn)擊下面鏈接下載最新 JAR
http://square.github.io/okhttp/#download
或者
GRADLE
在build.gradle中引用compile 'com.squareup.okhttp3:okhttp:(insert latest version)'

使用

在日常開發(fā)中最常用到的網(wǎng)絡(luò)請(qǐng)求就是GET和POST兩種請(qǐng)求方式。
HTTP GET
創(chuàng)建一個(gè)普通的同步get請(qǐng)求代碼如下:

String run(String url) throws IOException {
    Request request = new Request.Builder().url(url).build();
    OkHttpClient client = new OkHttpClient();
    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        return response.body().string();
    } else {
        throw new IOException("Unexpected code " + response);
    }
}

異步get:

Request request = new Request.Builder()
        .url("http://xxxxxx")
        .build();
OkHttpClient client = new OkHttpClient();
client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    }
    @Override
    public void onResponse(Call call, Response response) throws IOException {
        if(response.isSuccessful()){//回調(diào)的方法執(zhí)行在子線程。

        }
    }
});

HTTP POST
創(chuàng)建一個(gè)普通的post提交json數(shù)據(jù)代碼如下:

String post(String url, String json) throws IOException {
    RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);
    Request request = new Request.Builder()
            .url(url)
            .post(body)
            .build();
    OkHttpClient client = new OkHttpClient();
    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        return response.body().string();
    } else {
        throw new IOException("Unexpected code " + response);
    }
}

Request是OkHttp中訪問(wèn)的請(qǐng)求,Builder是輔助類,Response即OkHttp中的響應(yīng)。 從上面可以看出發(fā)起一個(gè)網(wǎng)絡(luò)請(qǐng)求主要三個(gè)步驟:1.創(chuàng)建OkHttpClient實(shí)例;2.使用構(gòu)造器創(chuàng)建請(qǐng)求;3.提交請(qǐng)求接收返回?cái)?shù)據(jù)。整個(gè)處理流程如下:


通過(guò)使用我們?cè)诰唧w分析下我們使用的功能在內(nèi)部是怎么實(shí)現(xiàn)的:

首先創(chuàng)建一個(gè)Request請(qǐng)求,設(shè)置相應(yīng)的url,和具體的請(qǐng)求參數(shù)和一些其他的配置,Request是通過(guò)Builder來(lái)完成構(gòu)建的。雖然說(shuō)是構(gòu)建Request,但實(shí)際上這些數(shù)據(jù)是不足以構(gòu)建一個(gè)合法的Request的,其他待補(bǔ)全的信息其實(shí)是OkHttp在后面某個(gè)環(huán)節(jié)幫你加上去,我們開發(fā)者只需要設(shè)置一些必要的參數(shù)就行了。

創(chuàng)建一個(gè)OkHttpClient 對(duì)象

OkHttpClient client = new OkHttpClient();

進(jìn)入源碼我們可以看到她的構(gòu)造方法

public OkHttpClient() {
    this(new Builder());
}
public Builder() {
  dispatcher = new Dispatcher(); //請(qǐng)求的執(zhí)行器,負(fù)責(zé)請(qǐng)求的同步執(zhí)行、異步執(zhí)行、取消、以及最多可以同時(shí)執(zhí)行的請(qǐng)求數(shù)
  protocols = DEFAULT_PROTOCOLS;  //支持的Protocol協(xié)議,Protocol和Url類似
  connectionSpecs = DEFAULT_CONNECTION_SPECS;  //支持套接字連接協(xié)議.
  eventListenerFactory = EventListener.factory(EventListener.NONE);
  proxySelector = ProxySelector.getDefault();  //代理服務(wù)器選擇器
  cookieJar = CookieJar.NO_COOKIES;  //提供cookie可持久化操作。
  socketFactory = SocketFactory.getDefault();  //創(chuàng)建套接字的工廠類
  hostnameVerifier = OkHostnameVerifier.INSTANCE;  //主機(jī)名驗(yàn)證
  certificatePinner = CertificatePinner.DEFAULT;  //約束所信任的證書
  proxyAuthenticator = Authenticator.NONE;  //身份驗(yàn)證代理服務(wù)器
  authenticator = Authenticator.NONE;  //身份驗(yàn)證代理服務(wù)器
  connectionPool = new ConnectionPool();  //連接池用于回收利用HTTP和HTTPS連接。
  dns = Dns.SYSTEM;   //dns服務(wù)器;默認(rèn)使用系統(tǒng)的
  followSslRedirects = true;  //是否支持sslhttp重定向,默認(rèn)true
  followRedirects = true;  //是否支持http重定向,默認(rèn)true
  retryOnConnectionFailure = true;  //連接失敗時(shí)是否重試,默認(rèn)true
  connectTimeout = 10_000;
  readTimeout = 10_000;
  writeTimeout = 10_000;
  pingInterval = 0;
}

為了方便我們使用,okhttp提供了一個(gè)“快捷操作”,全部使用了默認(rèn)的配置,比如指定了 Dispatcher (管理線程池)、鏈接池、超時(shí)時(shí)間等。OkHttpClient依然是通過(guò)Builder來(lái)進(jìn)行構(gòu)建的,我們開發(fā)者也可以自己去設(shè)置一些參數(shù),比如:

 HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
 loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
 OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .addInterceptor(new ParamInterceptor(mApplication))
            .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
            .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
            .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
            .build();

值得注意的是OKHttpClient實(shí)現(xiàn)了 Call.Factory接口,創(chuàng)建一個(gè)RealCall類的實(shí)例(Call的實(shí)現(xiàn)類)。前面的請(qǐng)求我們看到,在發(fā)送請(qǐng)求之前,需要調(diào)用newCall()方法,創(chuàng)建一個(gè)指向RealCall實(shí)現(xiàn)類的Call對(duì)象,實(shí)際上RealCall包裝了Request和OKHttpClient這兩個(gè)類的實(shí)例。使得后面的方法中可以很方便的使用這兩者。

創(chuàng)建完Request和OKHttpClient,接著我們看下具體是怎樣發(fā)起請(qǐng)求的,根據(jù)流程圖我們看到我們先有一個(gè)RealCall對(duì)象,接著看看RealCall對(duì)象是怎么被創(chuàng)建的,OKHttpClient實(shí)現(xiàn)了Call.Factory接口創(chuàng)建了一個(gè)call對(duì)象:

 /**
 * Prepares the {@code request} to be executed at some point in the future.
 */
@Override 
public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
}

通過(guò)前面代碼可以看到得到RealCall對(duì)象,執(zhí)行同步的execute()方法或者異步的enqueue我們就可以發(fā)起請(qǐng)求了,雖然只有一個(gè)簡(jiǎn)簡(jiǎn)單單的方法,但是這里面卻是最為復(fù)雜的,我們來(lái)看看我 RealCall里面的execute()方法和enqueue方法:

@Override 
public Response execute() throws IOException {
    synchronized (this) {
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
        client.dispatcher().executed(this);
        Response result = getResponseWithInterceptorChain();
        if (result == null) throw new IOException("Canceled");
        return result;
    } catch (IOException e) {
        eventListener.callFailed(this, e);
        throw e;
    } finally {
        client.dispatcher().finished(this);
    }
}
@Override 
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));
}

enqueue方法里面引入了一個(gè)新的類AsyncCall,這個(gè)類繼承于NamedRunnable,實(shí)現(xiàn)了Runnable接口。NamedRunnable可以給當(dāng)前的線程設(shè)置名字,并且用模板方法將線程的執(zhí)行體放到了execute方法中。

enqueue/execute 方法中涉及好幾個(gè)新的類和方法,如下:

同步 execute:

captureCallStackTrace()
dispatcher()
getResponseWithInterceptorChain()

異步 enqueue:

captureCallStackTrace()
dispatcher()
AsyncCall

我們看到不管是同步還是異步都會(huì)調(diào)用兩個(gè)相同的方法 captureCallStackTrace() 和 dispatcher() ,先看下 captureCallStackTrace() ,通過(guò)看源碼這個(gè)應(yīng)該是追蹤棧信息的。

Dispatcher是做什么的呢?文章開頭就提到了okhttp有個(gè)優(yōu)勢(shì)就是內(nèi)置連接池,支持連接復(fù)用,減少延遲。Dispatcher類就是負(fù)責(zé)管理調(diào)度的,我們可以看看Dispatcher的源碼:

public final class Dispatcher {
    /** 最大并發(fā)請(qǐng)求數(shù)為64 */
    private int maxRequests = 64;
    /** 每個(gè)主機(jī)最大請(qǐng)求數(shù)為5 */
    private int maxRequestsPerHost = 5;
    private @Nullable Runnable idleCallback;

    /** 線程池 */
    private @Nullable ExecutorService executorService;

    /** 準(zhǔn)備執(zhí)行的異步請(qǐng)求 */
    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

    /** 正在執(zhí)行的異步請(qǐng)求,包含已經(jīng)取消但未執(zhí)行完的請(qǐng)求 */
    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

    /** 正在執(zhí)行的同步請(qǐng)求,包含已經(jīng)取消單未執(zhí)行完的請(qǐng)求 */
    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<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
        }
        return executorService;
    }
    .....
}

從ExecutorService executorService()方法可以看出線程池實(shí)現(xiàn)了對(duì)象復(fù)用,降低線程創(chuàng)建開銷,從設(shè)計(jì)模式上來(lái)講,使用了享元模式。

代碼太多了,我只貼出了部分,有興趣大家可以去研究下,代碼不多。言歸正傳,從源碼我們可以看出Dispatcher就像是一個(gè)調(diào)度中心,這里負(fù)責(zé)所有的任務(wù)執(zhí)行,值得注意的是我們看到executorService這個(gè)對(duì)象是個(gè)線程池,配置如下:

核心線程數(shù):0
最大線程數(shù):Iteger.MAX_VALUE,其實(shí)就是不限制
空閑線程保活時(shí)間:60s
任務(wù)隊(duì)列:SynchronousQueue(沒有緩存大小的阻塞隊(duì)列),為有界隊(duì)列,且隊(duì)列大小為 0,不存 Runnable,只是用來(lái)進(jìn)行生產(chǎn)者和消費(fèi)者之間的傳遞任務(wù)。

實(shí)際上這里和我們?cè)?Executors 使用的 緩存線程池的配置完全一樣的,唯一的區(qū)別僅僅是修改了線程名,我們簡(jiǎn)要分析一下緩存線程池的運(yùn)行機(jī)制,進(jìn)入到 ThreadPoolExecutor 中的 execute 方法的源碼中,假設(shè)線程池處于運(yùn)行狀態(tài),會(huì)有這樣的流程:

新過(guò)來(lái)一個(gè)任務(wù),由于核心線程數(shù)為 0,不需要?jiǎng)?chuàng)建核心線程,所以嘗試加入任務(wù)隊(duì)列
如果線程池中有線程剛好空閑可以接收任務(wù),因?yàn)槿蝿?wù)隊(duì)列的類型是 SynchronousQueue,實(shí)際大小為 0,只做消費(fèi)者和生產(chǎn)者的中轉(zhuǎn)站,所以這時(shí)候,就可以入列成功,通過(guò) SynchronousQueue 中轉(zhuǎn)任務(wù)給空閑的線程執(zhí)行。
如果當(dāng)前線程池所有線程工作飽和,會(huì)入列失敗。這時(shí)候 ThreadPoolExecutor 會(huì)調(diào)用 addWorker(command, false) 來(lái)創(chuàng)建并且啟動(dòng)新線程。

如果這樣就會(huì)產(chǎn)生一個(gè)問(wèn)題,如果客戶端一次發(fā)起1000個(gè)請(qǐng)求,那肯定就會(huì)產(chǎn)生1000個(gè)線程。所以在 OkHttp 中線程池只是一個(gè)輔助作用,僅僅是用來(lái)做線程緩存,便于復(fù)用的。真正對(duì)這些請(qǐng)求的并發(fā)數(shù)量限制,執(zhí)行時(shí)機(jī)等等都是調(diào)度器 Dispatcher 承擔(dān)的。在 OkHttp 這里,線程池只是個(gè)帶緩存功能的執(zhí)行器,而真正的調(diào)度是外部包了一個(gè)調(diào)度策略的:

首先,是線程數(shù)量的約束。最大并發(fā)數(shù) maxRequests 默認(rèn)為 64,單個(gè)主機(jī)最大請(qǐng)求數(shù) maxRequestsPerHost 默認(rèn)為 5 個(gè)。所以極端情況下,才會(huì)開啟 64 個(gè)線程。一般場(chǎng)景是不會(huì)出現(xiàn)的,如果超過(guò)了約束,我們從代碼里面可以看到有兩個(gè)異步請(qǐng)求隊(duì)列readyAsyncCalls 和runningAsyncCalls ,如果沒有超過(guò)約束則把異步請(qǐng)求加入 runningAsyncCalls ,并在線程池中執(zhí)行(線程池會(huì)根據(jù)當(dāng)前負(fù)載自動(dòng)創(chuàng)建,銷毀,緩存相應(yīng)的線程)。否則就會(huì)加入 readyAsyncCalls 緩沖排隊(duì)。

那么,等待隊(duì)列中的請(qǐng)求什么時(shí)候會(huì)執(zhí)行呢?

回到之前提到的 AsyncCalls 中,在 execute 結(jié)束的時(shí)候會(huì)再調(diào)用一下 Dispatcher 的 finished 方法,如下

繼續(xù)看源碼我們會(huì)看到在finished方法中會(huì)執(zhí)行一個(gè)方法


所以,OkHttp 的調(diào)度器默認(rèn)配置下,在保證每個(gè)域名可以快速響應(yīng)請(qǐng)求的情況下,還限制了每個(gè)域名的并發(fā)數(shù)和總體的并發(fā)數(shù)。實(shí)際每個(gè)應(yīng)用的請(qǐng)求用到的域名都不多。線程池僅僅做線程緩存功能,調(diào)度策略應(yīng)該外部自己去實(shí)現(xiàn)。

接著我們來(lái)分析下getResponseWithInterceptorChain()

getResponseWithInterceptorChain()應(yīng)該是最為復(fù)雜的,我們先來(lái)看看源碼:

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

}

OkHttp 開發(fā)者之一介紹 OkHttp 的文章里面,作者講到:the whole thing is just a stack of built-in interceptors.
可見 Interceptor 是 OkHttp 最核心的一個(gè)東西,不要誤以為它只負(fù)責(zé)攔截請(qǐng)求進(jìn)行一些額外的處理(例如 cookie),實(shí)際上它把實(shí)際的網(wǎng)絡(luò)請(qǐng)求、緩存、透明壓縮等功能都統(tǒng)一了起來(lái),每一個(gè)功能都只是一個(gè) Interceptor,它們?cè)龠B接成一個(gè) Interceptor.Chain,環(huán)環(huán)相扣,最終圓滿完成一次網(wǎng)絡(luò)請(qǐng)求。

從上面我們可以分析出,其邏輯大致分為兩部分:

1、創(chuàng)建一系列攔截器,并將其放入一個(gè)攔截器數(shù)組中。這部分?jǐn)r截器即包括用戶自定義的攔截器也包括框架內(nèi)部攔截器
2、創(chuàng)建一個(gè)攔截器鏈RealInterceptorChain,并執(zhí)行攔截器鏈的proceed方法

這里okhttp用到了一個(gè)很經(jīng)典的設(shè)計(jì)模式:責(zé)任鏈模式,它在okhttp中得到了很好的實(shí)踐。實(shí)際上責(zé)任鏈模式在安卓系統(tǒng)中也有比較典型的實(shí)踐,例如 view 系統(tǒng)對(duì)點(diǎn)擊事件(TouchEvent)的處理,有興趣可以去看下。

從 getResponseWithInterceptorChain 函數(shù)我們可以看到,借用一張圖,Interceptor.Chain 的分布依次是:


從流程圖我們可以看出

1、首先是在創(chuàng)建OkHttpClient 自定義的interceptors
2、用來(lái)實(shí)現(xiàn)連接失敗的重試和重定向的RetryAndFollowUpInterceptor(在網(wǎng)絡(luò)請(qǐng)求失敗后進(jìn)行重試
當(dāng)服務(wù)器返回當(dāng)前請(qǐng)求需要進(jìn)行重定向時(shí)直接發(fā)起新的請(qǐng)求,并在條件允許情況下復(fù)用當(dāng)前連接)
3、用來(lái)修改請(qǐng)求和響應(yīng)的 header 信息的BridgeInterceptor(設(shè)置內(nèi)容長(zhǎng)度,內(nèi)容編碼
設(shè)置gzip壓縮,并在接收到內(nèi)容后進(jìn)行解壓。省去了應(yīng)用層處理數(shù)據(jù)解壓的麻煩
添加cookie,設(shè)置其他報(bào)頭,如User-Agent,Host,Keep-alive等。其中Keep-Alive是實(shí)現(xiàn)多路復(fù)用的必要步驟)
4、負(fù)責(zé)讀取緩存直接返回、更新緩存的 CacheInterceptor(當(dāng)網(wǎng)絡(luò)請(qǐng)求有符合要求的Cache時(shí)直接返回Cache,當(dāng)服務(wù)器返回內(nèi)容有改變時(shí)更新當(dāng)前cache,如果當(dāng)前cache失效,刪除)
5、負(fù)責(zé)和服務(wù)器建立連接的 ConnectInterceptor(其實(shí)是調(diào)用了 StreamAllocation 的newStream 方法來(lái)打開連接的。建聯(lián)的 TCP 握手,TLS 握手都發(fā)生該階段。過(guò)了這個(gè)階段,和服務(wù)端的 socket 連接打通)
6、配置 OkHttpClient 時(shí)設(shè)置的 networkInterceptors
7、負(fù)責(zé)向服務(wù)器發(fā)送請(qǐng)求數(shù)據(jù)、從服務(wù)器讀取響應(yīng)數(shù)據(jù)的 CallServerInterceptor(上一個(gè)階段已經(jīng)握手成功,HttpStream 流已經(jīng)打開,所以這個(gè)階段把 Request 的請(qǐng)求信息傳入流中,并且從流中讀取數(shù)據(jù)封裝成 Response 返回)

這就構(gòu)成了okhttp最核心的一塊東西,把Request 變成 Response。每個(gè) Interceptor 都可能完成這件事,所以我們循著鏈條讓每個(gè) Interceptor 自行決定能否完成任務(wù)以及怎么完成任務(wù)(自力更生或者交給下一個(gè) Interceptor)。這樣一來(lái),完成網(wǎng)絡(luò)請(qǐng)求這件事就徹底從 RealCall 類中剝離了出來(lái),簡(jiǎn)化了各自的責(zé)任和邏輯。

在這里我們簡(jiǎn)單分析下CallServerInterceptor,看看 OkHttp 是怎么進(jìn)行和服務(wù)器的實(shí)際通信的

CallServerInterceptor###

  @Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();

long sentRequestMillis = System.currentTimeMillis();

realChain.eventListener().requestHeadersStart(realChain.call());
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);

Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
  // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
  // Continue" response before transmitting the request body. If we don't get that, return
  // what we did get (such as a 4xx response) without ever transmitting the request body.
  if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
    httpCodec.flushRequest();
    realChain.eventListener().responseHeadersStart(realChain.call());
    responseBuilder = httpCodec.readResponseHeaders(true);
  }

  if (responseBuilder == null) {
    // Write the request body if the "Expect: 100-continue" expectation was met.
    realChain.eventListener().requestBodyStart(realChain.call());
    long contentLength = request.body().contentLength();
    CountingSink requestBodyOut =
        new CountingSink(httpCodec.createRequestBody(request, contentLength));
    BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);

    request.body().writeTo(bufferedRequestBody);
    bufferedRequestBody.close();
    realChain.eventListener()
        .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
  } else if (!connection.isMultiplexed()) {
    // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
    // from being reused. Otherwise we're still obligated to transmit the request body to
    // leave the connection in a consistent state.
    streamAllocation.noNewStreams();
  }
}

httpCodec.finishRequest();

if (responseBuilder == null) {
  realChain.eventListener().responseHeadersStart(realChain.call());
  responseBuilder = httpCodec.readResponseHeaders(false);
}

Response response = responseBuilder
    .request(request)
    .handshake(streamAllocation.connection().handshake())
    .sentRequestAtMillis(sentRequestMillis)
    .receivedResponseAtMillis(System.currentTimeMillis())
    .build();

int code = response.code();
if (code == 100) {
  // server sent a 100-continue even though we did not request one.
  // try again to read the actual response
  responseBuilder = httpCodec.readResponseHeaders(false);

  response = responseBuilder
          .request(request)
          .handshake(streamAllocation.connection().handshake())
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();

  code = response.code();
}

realChain.eventListener()
        .responseHeadersEnd(realChain.call(), response);

if (forWebSocket && code == 101) {
  // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
  response = response.newBuilder()
      .body(Util.EMPTY_RESPONSE)
      .build();
} else {
  response = response.newBuilder()
      .body(httpCodec.openResponseBody(response))
      .build();
}

if ("close".equalsIgnoreCase(response.request().header("Connection"))
    || "close".equalsIgnoreCase(response.header("Connection"))) {
  streamAllocation.noNewStreams();
}

if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
  throw new ProtocolException(
      "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}

return response;
}

我們抓住主干部分:

首先向服務(wù)器發(fā)送 request header和request body;
讀取 response header,先構(gòu)造一個(gè) Response 對(duì)象;
如果有 response body,就在 3 的基礎(chǔ)上加上 body 構(gòu)造一個(gè)新的 Response 對(duì)象;

這里我們可以看到,核心工作都由 HttpCodec 對(duì)象完成,而 HttpCodec 實(shí)際上利用的是 Okio,而 Okio 實(shí)際上還是用的 Socket。

我們?cè)賮?lái)說(shuō)說(shuō)自定義Interceptor ,我們可以用自定義的Interceptor 來(lái)干什么?比如,像我們請(qǐng)求接口都要加上token或者一些其他的一些公共參數(shù),我們就可以利用自定義Interceptor 來(lái)實(shí)現(xiàn):

public class ParamInterceptor implements Interceptor {

 private Context mContext;

 public ParamInterceptor(Context context ) {
    mContext = context;
  }


@Override
public Response intercept(Chain chain) throws IOException {
    Request oldrequest = chain.request();
    Request.Builder requestBuilder = oldrequest.newBuilder()
            .addHeader("Content-Type", "application/json");
    if (isLogin) {
        requestBuilder.addHeader("TOKEN","登錄返回的token");
    }
    Request request = requestBuilder.build();
    return chain.proceed(request);
  } }

這樣就實(shí)現(xiàn)了添加公共參數(shù)的功能

其實(shí) Interceptor 的設(shè)計(jì)也是一種分層的思想,每個(gè) Interceptor 就是一層。為什么要套這么多層呢?分層的思想在 TCP/IP 協(xié)議中就體現(xiàn)得淋漓盡致,分層簡(jiǎn)化了每一層的邏輯,每層只需要關(guān)注自己的責(zé)任(單一原則思想也在此體現(xiàn)),而各層之間通過(guò)約定的接口/協(xié)議進(jìn)行合作(面向接口編程思想),共同完成復(fù)雜的任務(wù)。

總結(jié)

OkHttp 還有很多細(xì)節(jié)部分沒有在本文探討,但建立一個(gè)清晰的概覽非常重要。對(duì)整體有了清晰認(rèn)識(shí)之后,細(xì)節(jié)部分如有需要,再單獨(dú)深入將更加容易。下一篇文章我們接著介紹Retrofit。

最后編輯于
?著作權(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)容

  • OkHttp解析系列 OkHttp解析(一)從用法看清原理OkHttp解析(二)網(wǎng)絡(luò)連接OkHttp解析(三)關(guān)于...
    Hohohong閱讀 21,121評(píng)論 4 58
  • 前言 用OkHttp很久了,也看了很多人寫的源碼分析,在這里結(jié)合自己的感悟,記錄一下對(duì)OkHttp源碼理解的幾點(diǎn)心...
    Java小鋪閱讀 1,607評(píng)論 0 13
  • 這段時(shí)間老李的新公司要更換網(wǎng)絡(luò)層,知道現(xiàn)在主流網(wǎng)絡(luò)層的模式是RxJava+Retrofit+OKHttp,所以老李...
    隔壁老李頭閱讀 33,890評(píng)論 51 405
  • 簡(jiǎn)介 OkHttp 是一款用于 Android 和 Java 的網(wǎng)絡(luò)請(qǐng)求庫(kù),也是目前 Android 中最火的一個(gè)...
    然則閱讀 1,498評(píng)論 1 39
  • 這篇文章主要講 Android 網(wǎng)絡(luò)請(qǐng)求時(shí)所使用到的各個(gè)請(qǐng)求庫(kù)的關(guān)系,以及 OkHttp3 的介紹。(如理解有誤,...
    小莊bb閱讀 1,322評(píng)論 0 4

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