OkHttp-3.12.x

0.簡介

概括起來說OkHttp是一款優秀的HTTP框架,它支持get請求和post請求,支持基於Http的文件上傳和下載,支持加載圖片,支持下載文件透明的GZIP壓縮,支持響應緩存避免重複的網絡請求,支持使用連接池來降低響應延遲問題。

特點如下:

  • 基於socket,可以對http進行更完全的封裝
  • 支持HTTP/2,允許所有同一個主機地址的請求共享同一個socket連接
  • 連接池,減少請求延時
  • 透明的GZIP壓縮,減少響應數據的大小
  • 緩存響應內容,避免一些完全重複的請求
  • 當網絡出現問題的時候OkHttp依然堅守自己的職責,它會自動恢復一般的連接問題,如果你的服務有多個IP地址,當第一個IP請求失敗時,OkHttp會交替嘗試你配置的其他IP,OkHttp使用現代TLS技術(SNI, ALPN)初始化新的連接,當握手失敗時會回退到TLS 1.0。

1.從demo說起

先要說一下,是怎麼使用的

package com.meituan.okhttp.client;

import com.meituan.okhttp.client.interceptor.LoggingInterceptor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okio.BufferedSink;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;

@Slf4j
public class OkHttpRequest {
	private static final String URL = "http://www.baidu.com";
	private static final MediaType MEDIA_TYPE_JSON = MediaType
			.parse("application/json; charset = utf-8");

	//OkHttpClient client = new OkHttpClient();
	OkHttpClient client = new OkHttpClient.Builder().build();

	public void syncGet() {//同步get方法
		Request request = new Request.Builder().url(URL).build();
		final Call call = client.newCall(request);

		//因爲同步阻塞會阻塞主方法的運行,所以另起線程,當然也可以用線程池,還不如用異步get方法
		new Thread(new Runnable() {
			public void run() {
				try {
					Response response = call.execute();//調用call拿到返回結果
					log.info("syncGet: " + response.body().string());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}

	public void aSyncGet() {//異步get方法
		Request request = new Request.Builder()
				.url(URL)
				.get()//默認是get 可以不寫
				.build();
		Call call = client.newCall(request);

		call.enqueue(new Callback() {//異步發起的請求會被加入到隊列中通過線程池來執行。通過回調方式拿到結果
			public void onFailure(Call call, IOException e) {
				log.info("onFailure");
			}

			public void onResponse(Call call, Response response)
					throws IOException {
				Request request1 = call.request();//這個只是說明可以拿到request。
				log.info("request: " + request1);
				log.info("aSyncGet onSuccess: " + response.body().string());
			}
		});

	}

	public void aSyncPost() {//異步post方法
		RequestBody requestBody = new RequestBody() {
			@Nullable
			@Override
			public MediaType contentType() {
				return MediaType
						.parse("application/json; charset = utf-8");
			}

			@Override
			public void writeTo(@NotNull BufferedSink bufferedSink)
					throws IOException {
				bufferedSink.write("zcz".getBytes());

			}
		};
		Request request = new Request.Builder()
				.url(URL)
				.post(requestBody)
				.build();

		client.newCall(request).enqueue(new Callback() {
			@Override
			public void onFailure(@NotNull Call call,
			                      @NotNull IOException e) {
				log.info("onFailure: post");
			}

			@Override
			public void onResponse(@NotNull Call call,
			                       @NotNull Response response) throws IOException {
				log.info("onSuccess: post" + response.body().string());
			}
		});
	}

	public void testInterceptor() {//添加自定義攔截器
		OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new LoggingInterceptor()).build();
		Request request = new Request.Builder()
				.url("http://www.publicobject.com/helloworld.txt")
				.header("User-Agent", "OkHttp Example")
				.build();
		okHttpClient.newCall(request).enqueue(new Callback() {
			@Override
			public void onFailure(Call call, IOException e) {
				log.info("onFailure: " + e.getMessage());
			}

			@Override
			public void onResponse(Call call, Response response) throws IOException {
				ResponseBody body = response.body();
				if (body != null) {
					log.info("onResponse: " + response.body().string());
					body.close();
				}
			}
		});

	}
}

這裏get方法的同步和異步只體現在Call這個對象上,只是調用的方法不同,具體有什麼不同呢?這個以後再說。還有一個需要注意的地方就是異步方法通過回調方式拿到Response。

2. OkHttpClient源碼解析

1)有兩種方式創建OkHttpClient:

OkHttpClient client = new OkHttpClient();
OkHttpClient client = new OkHttpClient.Builder().build();

其實推薦讓 OkHttpClient 保持單例,用同一個 OkHttpClient 實例來執行你的所有請求,因爲每一個 OkHttpClient 實例都擁有自己的連接池和線程池,重用這些資源可以減少延時和節省資源,如果爲每個請求創建一個 OkHttpClient 實例,顯然就是一種資源的浪費。

第一種沒什麼說的(屬性保持默認)。

說一下第二種,看到這個你會想到什麼呢?沒錯!就是builder(建造者)模式,那麼問題來了,

2)爲什麼要用建造者模式?

首先要了解建造者模式。設想一個場景:當一個類有很多屬性要初始化,並且大多都保持默認,只需要變動其中的某個或者某幾個,如果普通方法需要創建很多的重載的構造方法。建造者模式就是專門來解決這個痛點的。那麼怎麼解決的呢?

一般情況下,有一個內部類通常叫做Builder,其屬性與外部類相同,內部類的的“set方法”,除了set屬性之外,還會返回本身。這樣做的目的就是可以“鏈式”設置屬性。外部類的構造方法就可以只傳入這個內部類(外部類.屬性=內部類.屬性),而內部類的builder()方法對外部類進行初始化(調用外部類的構造方法),並返回外部類。沒錯!OkHttpClient就是使用了這種方式,理解了這個,就好理解OkHttpClient這個類了

放源代碼太麻煩,放一張結構圖,就是上面所述的那樣。

3)看看有哪些屬性

okhttp3.OkHttpClient:

final Dispatcher dispatcher;    // 調度器
    final @Nullable
    Proxy proxy; // 代理
    final List<Protocol> protocols;  // 協議
    final List<ConnectionSpec> connectionSpecs;  // 傳輸層版本和連接協議
    final List<Interceptor> interceptors;  // 攔截器
    final List<Interceptor> networkInterceptors;  // 網絡攔截器
    final EventListener.Factory eventListenerFactory;
    final ProxySelector proxySelector; // 代理選擇器
    final CookieJar cookieJar;  // cookie
    final @Nullable
    Cache cache;  // 緩存
    final @Nullable
    InternalCache internalCache;  // 內部緩存
    final SocketFactory socketFactory;  // socket 工廠
    final SSLSocketFactory sslSocketFactory;  // 安全套接層 socket 工廠,用於 https
    final CertificateChainCleaner certificateChainCleaner; // 驗證確認響應證書 適用 HTTPS 請求連接的主機名
    final HostnameVerifier hostnameVerifier; // 主機名字驗證
    final CertificatePinner certificatePinner; // 證書鏈
    final Authenticator proxyAuthenticator; // 代理身份驗證
    final Authenticator authenticator; // 本地身份驗證
    final ConnectionPool connectionPool;  // 連接池
    final Dns dns;  // 域名
    final boolean followSslRedirects;  // 安全套接層重定向
    final boolean followRedirects;  // 本地重定向
    final boolean retryOnConnectionFailure;  // 重試連接失敗
    final int callTimeout;
    final int connectTimeout;
    final int readTimeout;
    final int writeTimeout;
    final int pingInterval;

沒錯真的有很多屬性!屬性都有註釋,不一一解釋,感興趣的可以細看,注意的是緩存,攔截器,鏈接池等字眼,以後再解釋!

4)看看Builder這個內部類

okhttp3.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;
        ProxySelector proxySelector;
        CookieJar cookieJar;
        @Nullable
        Cache cache;
        @Nullable
        InternalCache internalCache;
        SocketFactory socketFactory;
        @Nullable
        SSLSocketFactory sslSocketFactory;
        @Nullable
        CertificateChainCleaner certificateChainCleaner;
        HostnameVerifier hostnameVerifier;
        CertificatePinner certificatePinner;
        Authenticator proxyAuthenticator;
        Authenticator authenticator;
        ConnectionPool connectionPool;
        Dns dns;
        boolean followSslRedirects;
        boolean followRedirects;
        boolean retryOnConnectionFailure;
        int callTimeout;
        int connectTimeout;
        int readTimeout;
        int writeTimeout;
        int pingInterval;

        public Builder() {
            dispatcher = new Dispatcher();//任務調度器
            protocols = DEFAULT_PROTOCOLS;//協議
            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;
        }
        public Builder addInterceptor(Interceptor interceptor) {
            if (interceptor == null)
                throw new IllegalArgumentException("interceptor == null");
            interceptors.add(interceptor);
            return this;
        }

屬性一一對應。設值返回this,就像之前所講的。

5)再看看OkHttpClient構造方法

okhttp3.OkHttpClient#OkHttpClient

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.eventListenerFactory = builder.eventListenerFactory;
        this.proxySelector = builder.proxySelector;
        this.cookieJar = builder.cookieJar;
        this.cache = builder.cache;
        this.internalCache = builder.internalCache;
        this.socketFactory = builder.socketFactory;

        boolean isTLS = false;
        for (ConnectionSpec spec : connectionSpecs) {
            isTLS = isTLS || spec.isTls();
        }

        if (builder.sslSocketFactory != null || !isTLS) {
            this.sslSocketFactory = builder.sslSocketFactory;
            this.certificateChainCleaner = builder.certificateChainCleaner;
        } else {
            X509TrustManager trustManager = Util.platformTrustManager();
            this.sslSocketFactory = newSslSocketFactory(trustManager);
            this.certificateChainCleaner = CertificateChainCleaner
                    .get(trustManager);
        }

        if (sslSocketFactory != null) {
            Platform.get().configureSslSocketFactory(sslSocketFactory);
        }

        this.hostnameVerifier = builder.hostnameVerifier;
        this.certificatePinner = builder.certificatePinner
                .withCertificateChainCleaner(certificateChainCleaner);
        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.callTimeout = builder.callTimeout;
        this.connectTimeout = builder.connectTimeout;
        this.readTimeout = builder.readTimeout;
        this.writeTimeout = builder.writeTimeout;
        this.pingInterval = builder.pingInterval;

        if (interceptors.contains(null)) {
            throw new IllegalStateException(
                    "Null interceptor: " + interceptors);
        }
        if (networkInterceptors.contains(null)) {
            throw new IllegalStateException(
                    "Null network interceptor: " + networkInterceptors);
        }
    }

這個類到此爲止,感興趣可以自己看。

3.Request源碼解析

由於也使用了建造者模式,細節就不分析了,只分析屬性,大部分可以見名知意,難理解的已經標註

public final class Request {
    final HttpUrl url;
    final String method;
    final Headers headers;
    final @Nullable
    RequestBody body;
    //通過tags來同時取消多個請求。當你構建一請求時,使用RequestBuilder.tag(tag)來分配一個標籤。
    // 之後你就可以用OkHttpClient.cancel(tag)來取消所有帶有這個tag的call。
    final Map<Class<?>, Object> tags;

    //相當於請求頭中的Cache-Control:
    private volatile @Nullable CacheControl cacheControl; // Lazily initialized.

    Request(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;//方法,默認get
        this.headers = builder.headers.build();
        this.body = builder.body;
        this.tags = Util.immutableMap(builder.tags);
    }
  • 設置url。可以是String類型、URL類型和HttpUrl類型。最終都是用到HttpUrl類型。
  • 設置method,包含get、post方法等。默認的是get方法。post方法要傳RequestBody,類似的還有delete、put、patch。
  • 設置header,方法有addHeader(String name, String value)、 removeHeader(String name)、header(String name, String value)、headers(Headers headers)。headers(Headers,headers)調用之後其它的header都會被移除,只添加這一個header。而header(String name, String value)方法調用之後,其它與這個name同名的header都會被移除,只保留這一個header。
  • 設置tag,設置tag可以用來取消這一請求。如果未指定tag或者tag爲null,那麼這個request本身就會當做是一個tag用來被取消請求。
  • 設置cacheControl,這個是設置到請求頭中。用來替換其它name是"Cache-Control"的header。如果cacheControl是空的話就會移除請求頭中name是"Cache-Control"的header。

4.Call源碼解析

分析完OkHttpClient和Request,繼續看demo

1)首先看看是怎麼用的

 Call call = client.newCall(request);

每一個請求任務都封裝爲一個Call,其實現爲RealCall。

2)這個newCall方法來自於哪?

okhttp3.Call.Factory#newCall

public interface Call extends Cloneable {
...
interface Factory {
        Call newCall(Request request);
    }
}

3)OkHttpClient是怎麼調用的?

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
....
		@Override
    public Call newCall(Request request) {//實現接口,重寫方法
        return RealCall.newRealCall(this, request, false /* for web socket */);//默認實現
    }
}

其實是調用okhttp3.RealCall#newRealCall這個方法實現的。

		static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
        // Safely publish the Call instance to the EventListener.
        RealCall call = new RealCall(client, originalRequest, forWebSocket);//調用下面的方法
        call.eventListener = client.eventListenerFactory().create(call);//必定會添加事件監聽器,用於跟蹤等
        return call;
    }
    
    private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
        this.client = client;
        this.originalRequest = originalRequest;
        this.forWebSocket = forWebSocket;
        //創建了一個RetryAndFollowUpInterceptor,用於處理請求錯誤和重定向等,
        // 這是 Okhttp 框架的精髓 interceptor chain 中的一環,
        // 默認情況下也是第一個攔截器,除非調用 OkHttpClient.Builder#addInterceptor(Interceptor) 來添加全局的攔截器。
        this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
        this.timeout = new AsyncTimeout() {
            @Override
            protected void timedOut() {
                cancel();
            }
        };
        this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
    }

4)EventListener源碼

創建call的時候添加了一個事件監聽器,看看源碼是做什麼的,這裏包含了很多方法。其實可以理解爲"call"的生命週期。不仔細看了。

在OkHttpClient中,我們可以傳入EventListener的工廠方法,爲每一個請求創建一個EventListener,來接收非常細的事件回調

5.發起請求-同步get請求

1)如何發起請求?

看demo

Response response = call.execute();//調用call拿到返回結果

之前分析過,這裏的call其實是RealCall,直接看其execute()方法,okhttp3.RealCall#execute

/**
     * 檢測這個 call 是否已經執行了,保證每個 call 只能執行一次。
     * 通知 dispatcher 已經進入執行狀態,將 call 加入到 runningSyncCalls 隊列中。
     * 調用 getResponseWithInterceptorChain() 函數獲取 HTTP 返回結果。
     * 最後還要通知 dispatcher 自己已經執行完畢,將 call 從 runningSyncCalls 隊列中移除。
     * @return
     * @throws IOException
     */
    @Override
    public Response execute() throws IOException {//模板方法實現
        synchronized (this) {
            // 每個 call 只能執行一次
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }
        captureCallStackTrace();
        timeout.enter();
        eventListener.callStart(this);
        try {
            // 請求開始, 將自己加入到runningSyncCalls隊列中
            client.dispatcher().executed(this);
            // 通過一系列攔截器請求處理和響應處理得到最終的返回結果
            Response result = getResponseWithInterceptorChain();//調用 getResponseWithInterceptorChain()獲得響應內容
            if (result == null) throw new IOException("Canceled");
            return result;
        } catch (IOException e) {
            e = timeoutExit(e);
            eventListener.callFailed(this, e);
            throw e;
        } finally {
            // 請求完成, 將其從runningSyncCalls隊列中移除
            client.dispatcher().finished(this);
        }
    }

2)okhttp3.Dispatcher#executed

/** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

所以其實這裏只是加入到一個隊列裏面,並沒有發起請求,發起請求還是通過getResponseWithInterceptorChain()實現的。這裏先賣一下關子,一會再講這個方法,先講一下異步的方式,因爲都會調用這個方法。

6.發起請求-異步get請求

1)如何發起請求?

看demo

call.enqueue(new Callback() {//異步發起的請求會被加入到隊列中通過線程池來執行。通過回調方式拿到結果
			public void onFailure(Call call, IOException e) {
				log.info("onFailure");
			}

			public void onResponse(Call call, Response response)
					throws IOException {
				Request request1 = call.request();//這個只是說明可以拿到request。
				log.info("request: " + request1);
				log.info("aSyncGet onSuccess: " + response.body().string());
			}
		});

在異步請求中,我們通過Callback來獲得簡單清晰的請求回調(onFailure、onResponse)

其實是調用okhttp3.RealCall#enqueue這個方法實現的。

@Override
    public void enqueue(Callback responseCallback) {
        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");//每個請求只能執行一次
            executed = true;
        }
        captureCallStackTrace();//爲retryAndFollowUpInterceptor加入了一個用於追蹤堆棧信息的callStackTrace
        eventListener.callStart(this);//之前加的listener,在這裏調用
        //這裏創建了一個 AsyncCall 並將Callback傳入,接着再交給任務分發器 Dispatcher 來進一步處理。
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }

2)okhttp3.Dispatcher#enqueue

void enqueue(AsyncCall call) {
        // 將AsyncCall加入到準備異步調用的隊列中
        synchronized (this) {
            readyAsyncCalls.add(call);
        }
        promoteAndExecute();
    }

3)okhttp3.Dispatcher#promoteAndExecute

/**
     * Promotes eligible calls from {@link #readyAsyncCalls} to {@link #runningAsyncCalls} and runs
     * them on the executor service. Must not be called with synchronization because executing calls
     * can call into user code.
     * <p>
     * 將 readyAsyncCalls 中的任務移動到 runningAsyncCalls中,並交給線程池來執行。
     * <p>
     * 從準備異步請求的隊列中取出可以執行的請求(正在運行的異步請求不得超過64,同一個host下的異步請求不得超過5個),
     * 加入到 executableCalls 列表中。
     * 循環 executableCalls 取出請求 AsyncCall 對象,調用其 executeOn 方法。
     *
     * @return true if the dispatcher is currently running calls.
     */
    private boolean promoteAndExecute() {
        assert (!Thread.holdsLock(this));

        List<AsyncCall> executableCalls = new ArrayList<>();
        boolean isRunning;
        synchronized (this) {
            //若條件允許,將readyAsyncCalls中的任務移動到runningAsyncCalls中,並交給線程池執行
            for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
                AsyncCall asyncCall = i.next();

                if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.大於最大請求數64,跳出
                if (runningCallsForHost(asyncCall) >= maxRequestsPerHost)
                    continue; // Host max capacity.

                i.remove();
                executableCalls.add(asyncCall);
                runningAsyncCalls.add(asyncCall);
            }
            isRunning = runningCallsCount() > 0;
        }

        for (int i = 0, size = executableCalls.size(); i < size; i++) {
            AsyncCall asyncCall = executableCalls.get(i);
            asyncCall.executeOn(executorService());//傳入線程池
        }

        return isRunning;
    }

異步發起的請求會被加入到 Dispatcher 中的 runningAsyncCalls雙端隊列中通過線程池來執行。

4)executorService是個啥?

okhttp3.Dispatcher#executorService

 /**
     * 這個線程池沒有核心線程,線程數量沒有限制,空閒60s就會回收,適用於大量耗時較短的任務;
     * 與 Executors.newCachedThreadPool() 比較類似;
     * <p>
     * 雖然線程池無任務上限,但是Dispatcher對入口enqueue()進行了把關,
     * 最大的異步任務數默認是64,同一個主機默認是5,當然這兩個默認值是可以修改的,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;
    }

5)okhttp3.RealCall.AsyncCall#executeOn

void executeOn(ExecutorService executorService) {
            assert (!Thread.holdsLock(client.dispatcher()));
            boolean success = false;
            try {
                executorService.execute(this);//這裏的this指AsyncCall
                success = true;
            } catch (RejectedExecutionException e) {
                InterruptedIOException ioException = new InterruptedIOException("executor rejected");
                ioException.initCause(e);
                eventListener.callFailed(RealCall.this, ioException);
                responseCallback.onFailure(RealCall.this, ioException);
            } finally {
                if (!success) {
                    client.dispatcher().finished(this); // This call is no longer running!
                }
            }
        }

AsyncCall必然實現Runable接口,因此反過來看其run()方法,看引用(okhttp3.RealCall.AsyncCall#executeOn)知道AsyncCall其實是RealCall的一個內部類

現實是AsyncCall並沒有直接實現Runable接口

final class AsyncCall extends NamedRunnable {
...
}

但是其父類NamedRunnable實現了Runnable接口

/**
 * Runnable implementation which always sets its thread name.
 */
public abstract class NamedRunnable implements Runnable {
    protected final String name;

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

    @Override
    public final void run() {
        String oldName = Thread.currentThread().getName();
        Thread.currentThread().setName(name);//設置名字,方便監控
        try {
            execute();//模板方法,具體實現留給子類實現
        } finally {
            Thread.currentThread().setName(oldName);
        }
    }

    protected abstract void execute();
}

這是一個模板方法的典型實現,因此需要看子類的execute()方法

6)okhttp3.RealCall.AsyncCall#execute

 @Override
        protected void execute() {
            boolean signalledCallback = false;
            timeout.enter();
            try {
                //異步和同步走的是同樣的方式,只不過在子線程中執行
                // 請求網絡獲取結果
                Response response = getResponseWithInterceptorChain();//調用 getResponseWithInterceptorChain()獲得響應內容
                signalledCallback = true;//這個標記主要是避免異常時2次回調
                responseCallback.onResponse(RealCall.this, response);//回調Callback,將響應內容傳回去
            } catch (IOException e) {
                e = timeoutExit(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); //回調Callback告知失敗
                }
            } catch (Throwable t) {
                cancel();
                if (!signalledCallback) {
                    IOException canceledException = new IOException("canceled due to " + t);
                    responseCallback.onFailure(RealCall.this, canceledException);
                }
                throw t;
            } finally {
                //不管請求成功與否,都進行finished()操作
                // 調度完成,移出隊列
                client.dispatcher().finished(this);
            }
        }

好了,同步和異步都要調用okhttp3.RealCall#getResponseWithInterceptorChain這個方法獲取Response。

之前說了很多隊列,這裏總結一下,其實是三個隊列,都是在okhttp3.Dispatcher類裏面

7)okhttp3.Dispatcher屬性

public final class Dispatcher {
    private int maxRequests = 64;//最大請求數量
    private int maxRequestsPerHost = 5;//每臺主機最大的請求數量
    private @Nullable Runnable idleCallback;

    /**
     * Executes calls. Created lazily.
     */
    private @Nullable
    ExecutorService executorService;//線程池,跟CachedThreadPool非常類似,這種類型的線程池,適用於大量的耗時較短的異步任務。

 
    //異步請求,Dispatcher 是通過啓動 ExcuteService 執行,線程池的最大併發量 64,
    // 異步請求先放置在 readyAsyncCalls,可以執行時放到 runningAsyncCalls 中,執行結束從runningAsyncCalls 中移除。
    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();//準備執行的請求隊列

    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();//正在運行的請求隊列

    //同步請求,由於它是即時運行的, Dispatcher 只需要運行前請求前存儲到 runningSyncCalls,
    // 請求結束後從 runningSyncCalls 中移除即可。
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//一個正在運行的同步請求隊列
...
}

7 okhttp3.RealCall#getResponseWithInterceptorChain

/**
 * 這裏先是創建了一個 Interceptor 的ArrayLIst,然後將各類 interceptor 全部加入到ArrayLIst中,之後按照順序執行。包含以下 interceptor:
 *
 * interceptors:配置 OkHttpClient 時設置的 inteceptors
 * RetryAndFollowUpInterceptor:負責失敗重試以及重定向
 * BridgeInterceptor:負責把用戶構造的請求轉換爲發送到服務器的請求、把服務器返回的響應轉換爲用戶友好的響應
 * CacheInterceptor:負責讀取緩存直接返回、更新緩存
 * ConnectInterceptor:負責和服務器建立連接
 * networkInterceptors:配置 OkHttpClient 時設置的 networkInterceptors
 * CallServerInterceptor:負責向服務器發送請求數據、從服務器讀取響應數據
 * @return
 * @throws IOException
 */
//攔截器鏈
Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();//這是一個List,是有序的
    interceptors.addAll(client.interceptors());//首先添加的是用戶添加的全局攔截器
    interceptors.add(retryAndFollowUpInterceptor);//錯誤、重定向攔截器
    interceptors.add(new BridgeInterceptor(client.cookieJar()));//封裝request和response攔截器,橋接攔截器,橋接應用層與網絡層,添加必要的頭、
    //構造這個攔截器的時候傳入的是我們構造的OkHttpClient中設置的interanlCache,
    // 而當我們用默認方式構造OkHttpClient的時候是不會創建緩存的,也就是internalCache=null的
    interceptors.add(new CacheInterceptor(client.internalCache()));//緩存處理,Last-Modified、ETag、DiskLruCache等
    interceptors.add(new ConnectInterceptor(client));//負責和服務器建立連接攔截器
    //從這就知道,通過okHttpClient.Builder#addNetworkInterceptor()傳進來的攔截器只對非網頁的請求生效
    //配置 OkHttpClient 時設置的 networkInterceptors
    if (!forWebSocket) {
        interceptors.addAll(client.networkInterceptors());
    }
    //真正訪問服務器的攔截器
    //負責向服務器發送請求數據、從服務器讀取響應數據(實際網絡請求)
    interceptors.add(new CallServerInterceptor(forWebSocket));

			//注意這裏的0,即從頭開始執行,這裏傳入的參數很多都是null,之後再創建
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
            originalRequest, this, eventListener, client.connectTimeoutMillis(),
            client.readTimeoutMillis(), client.writeTimeoutMillis());

    //最後通過RealInterceptorChain#proceed(Request)來執行整個 interceptor chain
    Response response = chain.proceed(originalRequest);
    if (retryAndFollowUpInterceptor.isCanceled()) {
        closeQuietly(response);
        throw new IOException("Canceled");
    }
    return response;
}

OkHttp的攔截器鏈可謂是其整個框架的精髓,用戶可傳入的 interceptor 分爲兩類:
①一類是全局的 interceptor,該類 interceptor 在整個攔截器鏈中最早被調用,通過 OkHttpClient.Builder#addInterceptor(Interceptor) 傳入;
②另外一類是非網頁請求的 interceptor ,這類攔截器只會在非網頁請求中被調用,並且是在組裝完請求之後,真正發起網絡請求前被調用,所有的 interceptor 被保存在 List interceptors 集合中,按照添加順序來逐個調用,具體可參考 RealCall#getResponseWithInterceptorChain() 方法。通過 OkHttpClient.Builder#addNetworkInterceptor(Interceptor) 傳入;

這裏舉一個簡單的例子,例如有這樣一個需求,我要監控App通過 OkHttp 發出的所有原始請求,以及整個請求所耗費的時間,針對這樣的需求就可以使用第一類全局的 interceptor 在攔截器鏈頭去做。

在OkHttp3中,其靈活性很大程度上體現在可以 intercept 其任意一個環節,而這個優勢便是okhttp3整個請求響應架構體系的精髓所在,先放出一張主框架請求流程圖,接着再分析源碼。

8.OkHttp主流程

在這裏插入圖片描述
簡述 OkHttp 的請求流程:

  • OkHttpClient 實現了 Call.Fctory,負責爲 Request 創建 Call。

  • RealCall 是 Call 的具體實現,它的異步請求是通過 Dispatcher 調度器利用ExcutorService實現,而最終進行網絡請求時和同步請求一樣,都是通過 getResponseWithInterceptorChain 方法實現。

  • getResponseWithInterceptorChain 方法中採用了責任鏈模式,每一個攔截器各司其職,主要做兩件事。

攔截上一層攔截器封裝好的 Request,然後自身對這個 Request 進行處理,處理後向下傳遞。

接收下一層攔截器傳遞回來的 Response,然後自身對 Response 進行處理,返回給上一層。

1)各攔截器作用

retryAndFollowUpInterceptor——失敗和重定向過濾器BridgeInterceptor——封裝request和response過濾器CacheInterceptor——緩存相關的過濾器,負責讀取緩存直接返回、更新緩存ConnectInterceptor——負責和服務器建立連接,連接池等networkInterceptors——配置 OkHttpClient 時設置的 networkInterceptorsCallServerInterceptor——負責向服務器發送請求數據、從服務器讀取響應數據(實際網絡請求)

2)攔截器鏈設計好處

高內聚,低耦合,可擴展,單一職責。

9. okhttp3.Interceptor接口

/**
 * Observes, modifies, and potentially short-circuits requests going out and the corresponding
 * responses coming back in. Typically interceptors add, remove, or transform headers on the request
 * or response.
 */
public interface Interceptor {
    Response intercept(Chain chain) throws IOException;//主要方法

    interface Chain {
        Request request();

        Response proceed(Request request) throws IOException;

        /**
         * Returns the connection the request will be executed on. This is only available in the chains
         * of network interceptors; for application interceptors this is always null.
         */
        @Nullable
        Connection connection();

        Call call();

        int connectTimeoutMillis();

        Chain withConnectTimeout(int timeout, TimeUnit unit);

        int readTimeoutMillis();

        Chain withReadTimeout(int timeout, TimeUnit unit);

        int writeTimeoutMillis();

        Chain withWriteTimeout(int timeout, TimeUnit unit);
    }
}

各個攔截器都實現了這個接口,如果想自己擴展也可以實現該接口比如

10 自定義攔截器

com.meituan.okhttp.client.interceptor.LoggingInterceptor

package com.meituan.okhttp.client.interceptor;

import lombok.extern.slf4j.Slf4j;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
@Slf4j
public class LoggingInterceptor implements Interceptor {
	private static final String TAG = "LoggingInterceptor";
//	private static final Logger logger = LogManager.getLogger();
	@NotNull
	@Override
	public Response intercept(@NotNull Chain chain)
			throws IOException {
		Request request = chain.request();
		long startTime = System.nanoTime();
		Response response = chain.proceed(request);
		log.info("time" + (System.nanoTime()-startTime));
		return response;
	}

}

com.meituan.okhttp.client.OkHttpRequest#testInterceptor

public void testInterceptor() {//添加自定義攔截器
		OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new LoggingInterceptor()).build();
		Request request = new Request.Builder()
				.url("http://www.publicobject.com/helloworld.txt")
				.header("User-Agent", "OkHttp Example")
				.build();
		okHttpClient.newCall(request).enqueue(new Callback() {
			@Override
			public void onFailure(Call call, IOException e) {
				log.info("onFailure: " + e.getMessage());
			}

			@Override
			public void onResponse(Call call, Response response) throws IOException {
				ResponseBody body = response.body();
				if (body != null) {
					log.info("onResponse: " + response.body().string());
					body.close();
				}
			}
		});

	}

11 okhttp3.internal.http.RetryAndFollowUpInterceptor攔截器

其interceptor方法很長,但是簡化以後

1)簡化代碼

@Override public Response intercept(Chain chain) throws IOException {
    。。。
    while (true) {
    。。。
      try {
        response = realChain.proceed(request, streamAllocation, null, null);
      }
    。。。
    if(滿足條件){
        return response;
    }
    。。。
      //不滿足條件,一頓操作,賦值再來!
      request = followUp;
      priorResponse = response;
    }
  }

其實就流程來上,我認爲宏觀上代碼縮減到這樣就夠了,甚至可以再刪點,這裏先從流程上理解.其實代碼成這樣,基本上大家都能理解了,一個while(true)表明這是個循環體,循環體主要做的事可以看到其實是遞歸的主要方法。

response = realChain.proceed(request, streamAllocation, null, null);

執行了這個方法後,就會交給下一個過濾器繼續執行,所以單從這裏來看,我們可以簡單的理解爲這個過濾器其實沒做什麼。但是當出現了一些問題,導致不滿足條件的時候,就需要進行一系列的操作,重新複製Request,重新請求,這也就是while的功能,對應的也就是這個過濾器的主要功能:重試和重定向。這裏我們宏觀上已經對RetryAndFollowUpInterceptor有了一個基本的理解了。

2)過程細節

@Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Call call = realChain.call();
        EventListener eventListener = realChain.eventListener();

        //創建一個StreamAllocation,剛傳入的時候是null,這個類大概可以理解爲是處理Connections,Streams,Calls三者之間的關係
        StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
                createAddress(request.url()), call, eventListener, callStackTrace);
        this.streamAllocation = streamAllocation;

        //統計重定向次數,不能大於20
        int followUpCount = 0;
        Response priorResponse = null;
        while (true) {
            //取消
            if (canceled) {
                streamAllocation.release();
                throw new IOException("Canceled");
            }

            Response response;
            boolean releaseConnection = true;//初始化時賦值爲true
            try {
                //調用下一個interceptor的來獲得響應內容
                response = realChain.proceed(request, streamAllocation, null, null);
                releaseConnection = false;
            } catch (RouteException e) {
                // The attempt to connect via a route failed. The request will not have been sent.
                //嘗試連接一個路由失敗,這個請求還沒有被髮出
                if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
                    throw e.getFirstConnectException();
                }
                releaseConnection = false;
                continue;//重試
            } catch (IOException e) {
                // An attempt to communicate with a server failed. The request may have been sent.
                boolean requestSendStarted = !(e instanceof ConnectionShutdownException);//先判斷當前請求是否已經發送了
                //同樣的重試判斷
                if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
                releaseConnection = false;
                continue;
            } finally {
                // We're throwing an unchecked exception. Release any resources.
                //沒有捕獲到的異常,最終要釋放
                //由於releaseConnection初始化爲true,
                // 而當正常執行realChain.proceed或在執行過程中捕捉到異常時設置爲false,
                // 所以當執行過程中捕捉到沒有檢測到的異常時,需要釋放一些內容。

                if (releaseConnection) {
                    streamAllocation.streamFailed(null);
                    streamAllocation.release();
                }
            }

            // Attach the prior response if it exists. Such responses never have a body.
            //這裏基本上都沒有講,priorResponse是用來保存前一個Resposne的,
            // 這裏可以看到將前一個Response和當前的Resposne結合在一起了,
            // 對應的場景是,當獲得Resposne後,發現需要重定向,則將當前Resposne設置給priorResponse,再執行一遍流程,
            //直到不需要重定向了,則將priorResponse和Resposne結合起來。
            if (priorResponse != null) {
                response = response.newBuilder()
                        .priorResponse(priorResponse.newBuilder()
                                .body(null)
                                .build())
                        .build();
            }

            Request followUp;
            try {
                //重定向處理
                //判斷是否需要重定向,如果需要重定向則返回一個重定向的Request,沒有則爲null
                followUp = followUpRequest(response, streamAllocation.route());
            } catch (IOException e) {
                streamAllocation.release();
                throw e;
            }

            if (followUp == null) {//不需要重定向
                //是WebSocket,釋放
                streamAllocation.release();
                return response;//返回response
            }

            //需要重定向,關閉響應流
            closeQuietly(response.body());

            //重定向次數++,並且小於最大重定向次數MAX_FOLLOW_UPS(20)
            if (++followUpCount > MAX_FOLLOW_UPS) {
                streamAllocation.release();
                throw new ProtocolException("Too many follow-up requests: " + followUpCount);
            }
            //是UnrepeatableRequestBody, 剛纔看過也就是是流類型,沒有被緩存,不能重定向
            if (followUp.body() instanceof UnrepeatableRequestBody) {
                streamAllocation.release();
                throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
            }
            //判斷是否相同,不然重新創建一個streamConnection
            if (!sameConnection(response, followUp.url())) {
                streamAllocation.release();
                streamAllocation = new StreamAllocation(client.connectionPool(),
                        createAddress(followUp.url()), call, eventListener, callStackTrace);
                this.streamAllocation = streamAllocation;
            } else if (streamAllocation.codec() != null) {
                throw new IllegalStateException("Closing the body of " + response
                        + " didn't close its backing stream. Bad interceptor?");
            }
            //賦值再來!
            request = followUp;
            priorResponse = response;
        }
    }

3)okhttp3.internal.http.RetryAndFollowUpInterceptor#recover

/**
     * Report and attempt to recover from a failure to communicate with a server. Returns true if
     * {@code e} is recoverable, or false if the failure is permanent. Requests with a body can only
     * be recovered if the body is buffered or if the failure occurred before the request has been
     * sent.
     */
    private boolean recover(IOException e, StreamAllocation streamAllocation,
                            boolean requestSendStarted, Request userRequest) {
        streamAllocation.streamFailed(e);

        // The application layer has forbidden retries.
        //如果OkHttpClient直接配置拒絕失敗重連,return false,默認時true
        if (!client.retryOnConnectionFailure()) return false;

        // We can't send the request body again.
        //如果請求已經發送,並且這個請求體是一個UnrepeatableRequestBody類型,則不能重試。
        //StreamedRequestBody實現了UnrepeatableRequestBody接口,是個流類型,不會被緩存,所以只能執行一次,具體可看。
        if (requestSendStarted && requestIsUnrepeatable(e, userRequest)) return false;

        // This exception is fatal.
        //一些嚴重的問題,就不要重試了
        if (!isRecoverable(e, requestSendStarted)) return false;

        // No more routes to attempt.
        //沒有更多的路由就不要重試了
        if (!streamAllocation.hasMoreRoutes()) return false;

        // For failure recovery, use the same route selector with a new connection.
        return true;
    }

12.okhttp3.internal.cache.CacheInterceptor

/**
     * 1.通過Request嘗試到Cache中拿緩存(裏面非常多流程),當然前提是OkHttpClient中配置了緩存,默認是不支持的。
     * 2.根據response,time,request創建一個緩存策略,用於判斷怎樣使用緩存。
     * 3.如果緩存策略中設置禁止使用網絡,並且緩存又爲空,則構建一個Resposne直接返回,注意返回碼=504
     * 4.緩存策略中設置不使用網絡,但是又緩存,直接返回緩存
     * 5.接着走後續過濾器的流程,chain.proceed(networkRequest)
     * 6.當緩存存在的時候,如果網絡返回的Resposne爲304,則使用緩存的Resposne。
     * 7.構建網絡請求的Resposne
     * 8.當在OKHttpClient中配置了緩存,則將這個Resposne緩存起來。
     * 9.緩存起來的步驟也是先緩存header,再緩存body。
     * 10.返回Resposne。
     *
     *
     * @param chain
     * @return
     * @throws IOException
     */
    @Override
    public Response intercept(Chain chain) throws IOException {
        //1.嘗試通過這個Request拿緩存
        //默認cache爲null,可以配置cache,不爲空嘗試獲取緩存中的response
        Response cacheCandidate = cache != null
                ? cache.get(chain.request())
                : null;

        long now = System.currentTimeMillis();

        //根據response,time,request創建一個緩存策略,用於判斷怎樣使用緩存
        //對應的是CacheStrategy這個類,裏面主要涉及Http協議中緩存的相關設置
        CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
        Request networkRequest = strategy.networkRequest;
        Response cacheResponse = strategy.cacheResponse;

        if (cache != null) {
            cache.trackResponse(strategy);
        }

        if (cacheCandidate != null && cacheResponse == null) {
            closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
        }

        // If we're forbidden from using the network and the cache is insufficient, fail.
        //2.如果不允許使用網絡並且緩存爲空,新建一個504的Resposne返回。
        //如果緩存策略中禁止使用網絡,並且緩存又爲空,則構建一個Resposne直接返回,注意返回碼=504
        if (networkRequest == null && cacheResponse == null) {
            return new Response.Builder()
                    .request(chain.request())
                    .protocol(Protocol.HTTP_1_1)
                    .code(504)
                    .message("Unsatisfiable Request (only-if-cached)")
                    .body(Util.EMPTY_RESPONSE)
                    .sentRequestAtMillis(-1L)
                    .receivedResponseAtMillis(System.currentTimeMillis())
                    .build();
        }

        // If we don't need the network, we're done.
        //不使用網絡,但是又緩存,直接返回緩存
        if (networkRequest == null) {
            return cacheResponse.newBuilder()
                    .cacheResponse(stripBody(cacheResponse))
                    .build();
        }

        Response networkResponse = null;
        try {
            //調用下一個攔截器進行網絡請求
            //3.如果不允許使用網絡,但是有緩存,返回緩存。
            networkResponse = chain.proceed(networkRequest);
        } finally {
            // If we're crashing on I/O or otherwise, don't leak the cache body.
            if (networkResponse == null && cacheCandidate != null) {
                closeQuietly(cacheCandidate.body());
            }
        }

        // If we have a cache response too, then we're doing a conditional get.
        //4.鏈式調用下一個過濾器。
        networkResponse = chain.proceed(networkRequest);

        //當緩存響應和網絡響應同時存在的時候,選擇用哪個
        if (cacheResponse != null) {
            if (networkResponse.code() == HTTP_NOT_MODIFIED) {
                //如果返回碼是304,客戶端有緩衝的文檔併發出了一個條件性的請求(一般是提供If-Modified-Since頭表示客戶
                // 只想比指定日期更新的文檔)。服務器告訴客戶,原來緩衝的文檔還可以繼續使用。
                //則使用緩存的響應
                Response response = cacheResponse.newBuilder()
                        .headers(combine(cacheResponse.headers(), networkResponse.headers()))
                        .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
                        .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
                        .cacheResponse(stripBody(cacheResponse))
                        .networkResponse(stripBody(networkResponse))
                        .build();
                networkResponse.body().close();

                // Update the cache after combining headers but before stripping the
                // Content-Encoding header (as performed by initContentStream()).
                cache.trackConditionalCacheHit();
                cache.update(cacheResponse, response);
                return response;
            } else {
                closeQuietly(cacheResponse.body());
            }
        }

        //使用網絡響應
        //6、7.使用網絡請求得到的Resposne,並且將這個Resposne緩存起來(前提當然是能緩存)。
        // 接下來就是腦袋都大的細節了,我也不敢說分析的十分詳細,只能就我的理解總體分析,學習。
        Response response = networkResponse.newBuilder()
                .cacheResponse(stripBody(cacheResponse))
                .networkResponse(stripBody(networkResponse))
                .build();

        //所以默認創建的OkHttpClient是沒有緩存的
        //存緩存這個步驟的分析了,其實前面的取緩存的分析結束後,
        // 這裏對存緩存不難猜測其實是想對應的,也就比較好理解了,對應的大體應該是header存入clean[0],body存入clean[1]。
        if (cache != null) {
            //9。 將響應緩存
            if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
                // Offer this request to the cache.
                //緩存Resposne的Header信息
                CacheRequest cacheRequest = cache.put(response);
                //緩存body
                return cacheWritingResponse(cacheRequest, response);
            }

            //只能緩存GET....不然移除request
            if (HttpMethod.invalidatesCache(networkRequest.method())) {
                try {
                    cache.remove(networkRequest);
                } catch (IOException ignored) {
                    // The cache cannot be written.
                }
            }
        }

        return response;
    }

13.okhttp3.internal.connection.ConnectInterceptor

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

        // We need the network to satisfy this request. Possibly for validating a conditional GET.
        boolean doExtensiveHealthChecks = !request.method().equals("GET");
        //建立HttpCodec
        HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
        //獲取連接
        RealConnection connection = streamAllocation.connection();

        return realChain.proceed(request, streamAllocation, httpCodec, connection);
    }

14 okhttp3.internal.http.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();

        //開始寫入header
        realChain.eventListener().requestHeadersStart(realChain.call());
        //HttpCodec其實是一個接口,對應的使用策略模式分別根據是Http還是Http/2請求,這裏就看一下Http1Codec的實現吧。
        httpCodec.writeRequestHeaders(request);//1.開始寫入header
        //寫入結束
        realChain.eventListener().requestHeadersEnd(realChain.call(), request);

        Response.Builder responseBuilder = null;//首先構建了一個null的responseBuilder。
        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.
            //2.當Header爲Expect: 100-continue時,只發送請求頭
//            當Header爲Expect: 100-continue時,只發送請求頭
//            發送一個請求, 包含一個Expect:100-continue, 詢問Server使用願意接受數據
//            接收到Server返回的100-continue應答以後, 才把數據POST給Server

//            1.如果可以繼續請求,則responseBuilder=null
//            2.如果不行,則responseBuilder不爲空,並且爲返回的Header
            if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
                httpCodec.flushRequest();//刷新請求
                realChain.eventListener().responseHeadersStart(realChain.call());
                //讀取response的header信息,並返回一個responseBuilder賦值給responseBuilder。
                responseBuilder = httpCodec.readResponseHeaders(true);
            }

            //如果可以繼續請求,則Responsebuilder=null,執行if判斷裏的內容,可以看到就是對於請求體的寫入操作,當然任然是使用Okio進行寫入操作。
            if (responseBuilder == null) {
                // Write the request body if the "Expect: 100-continue" expectation was met.
                //得到響應後,根據Resposne判斷是否寫入請求體
                // 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);

                //3.寫入請求體
                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();
            }
        }

        //4.結束請求
        httpCodec.finishRequest();

        if (responseBuilder == null) {
            realChain.eventListener().responseHeadersStart(realChain.call());
            //5.得到響應頭
            responseBuilder = httpCodec.readResponseHeaders(false);
        }

        //6.構建初步響應
//        通過返回得到的responseBuilder構建攜帶有響應頭的Reponse。
        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);

//        剩下的其實就是對弈返回碼的判斷,對應正常的返回碼的話,構建響應體到Response中,最後將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;
    }

總結

1.首先發現了計算機網絡的重要性,Http協議,準備後面入一本書,再回顧學習一下。
2.線程安全各種方式,各種數據結構。
3.設計模式,粗略回顧一下:建造者模式,責任鏈模式,策略模式…其實不用強行使用設計模式,其實主要是設計思想
4.面向接口編程,這個不用說,越看越重要。
5.方法的命名,其實我感覺挺有趣,也挺講究的。
6.註釋,其實和5差不多,基本上就是編程風格了。

附件

1)demo

https://github.com/ouyangxizhu/okhttp-demo.git

2)origin-3.12.x

https://github.com/ouyangxizhu/okhttp-origin-3.12.x.git

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章