OkHttp
Retrofit使用OkHttp,Volley支援替換底層http棧為OkHttp,甚至Google的最新源碼裡,都用起了OkHttp,替換了原來用的HttpClient。
筆者雖然一直聽說OkHttp怎麼怎麼好,但始終雲裡霧裡究竟它如何優越,是以抽空瞄了瞄源碼,小小分析一下。
官網說了什麼
okhttp官網
- HTTP/2 和 SPDY 支援允許所有到同一個host的請求共享一個socket。
- 連接配接池減少請求延時 (如果 SPDY 不可用).
- 透明的GZIP支援,減少下載下傳大小。
- 緩存Response以減少網絡去做完全重複的請求。
OkHttp在網絡有問題的時候表現很好:
- 它會靜默從常見的連接配接問題中恢複。
- 如果你的服務有多個IP位址對應,OkHttp會在首次連接配接失敗的時候嘗試其他位址。
- OkHttp使用現代的TLS features (SNI, ALPN) 來初始化連接配接, 并在握手失敗的時候倒回到TLS 1.0。
OkHttp 2.0的API設計為流式builders和immutability,同時支援同步blocking call和異步帶callback的call。
你可以不用重寫網絡層代碼來試試OkHttp。okhttp-urlconnection子產品實作了大家熟悉的java.net.HttpURLConnection API,而okhttp-apache子產品實作了Apache HttpClient API.
OkHttp支援Android 2.3和以上,對Java要求至少1.7。
對了,OkHttp還用了Okio來做快速I/O和可調整大小的buffer。
使用
分别來看看Get和Post請求吧
Get請求并獲得response body:
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
Post請求,看上去和上面的差别隻在于Request的Builder多了.post(body):
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
分析
這麼多module,裡面的類也不算少,怎麼看起呢,不如就以使用裡面的代碼作為切入點吧。
OkHttpClient
作為入口,OkHttpClient裡面包含了各種東西,
OkHttpClient
:
// 之前提到的協定
private static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
Protocol.HTTP_2, Protocol.SPDY_3, Protocol.HTTP_1_1);
// TLS那些事
private static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT);
// 這裡實作了各種抽象方法,初始化了Intelnal單例,提供給各處使用
static {
Internal.instance = new Internal() {
}
/** 懶加載(HTTPS那些事) */
private static SSLSocketFactory defaultSslSocketFactory;
// 也是之前提過的,多個IP時候的選擇政策
private final RouteDatabase routeDatabase;
// 異步請求執行時候的政策,用到了ExecutorService作線程池,預設建立的是core 0,max Integer.MAX_VALUE,keep alive 60秒的線程池
private Dispatcher dispatcher;
// 代理嘛,大家都懂得,分為DIRECT(直連無代理)、HTTP和SOCKS
private Proxy proxy;
private List<Protocol> protocols;
private List<ConnectionSpec> connectionSpecs;
// 攔截器 - 在Response正式傳回前處理資料,比如Gzip解壓
private final List<Interceptor> interceptors = new ArrayList<>();
// 這個網絡攔截器可就牛逼了。。。相較前面那個發生地更早,在HttpEngine的readResponse中被調用
// 可以做諸如CheckHandshake初始化的時候加進去了一個握手的攔截器去檢查是否在黑名單;LoggingInterceptors則加了一個log的攔截器,輸出請求和response的資訊;Progress加了個包裝response的攔截器,來增加progress功能;RewriteResponseCacheControl直接重寫server的cache控制頭
private final List<Interceptor> networkInterceptors = new ArrayList<>();
// 代理伺服器選擇器
private ProxySelector proxySelector;
// Cookie處理器,可以接受get和put cookie的事件
private CookieHandler cookieHandler;
// 就是個接口
/** Non-null if this client is caching; possibly by {@code cache}. */
private InternalCache internalCache;
// 可以自定義,内部包含了上面的InternalCache實作
private Cache cache;
// 都是java的東西
private SocketFactory socketFactory;
private SSLSocketFactory sslSocketFactory;
private HostnameVerifier hostnameVerifier;
// 證書驗證
private CertificatePinner certificatePinner;
// 回複伺服器認證要求
private Authenticator authenticator;
// 連接配接池,同一個host的請求可能共享同一個connection,該類還實作了connection為以後的使用保持打開的政策
private ConnectionPool connectionPool;
// 接口,實際使用了InetAddress.getAllByName
private Network network;
private boolean followSslRedirects = true;
private boolean followRedirects = true;
private boolean retryOnConnectionFailure = true;
// 其實10_000就是10000,這裡是10000毫秒即10秒
private int connectTimeout = _000;
private int readTimeout = _000;
private int writeTimeout = _000;
再看看構造函數:
public OkHttpClient() {
routeDatabase = new RouteDatabase();
dispatcher = new Dispatcher();
}
private OkHttpClient(OkHttpClient okHttpClient) {
// 反正就是各種參數拷貝過來
}
那麼當我們調用
new OkHttpClient()
的時候,其實就是new了兩個成員變量。
嗯。。。好像很輕,還是看看具體的請求執行吧
Response response = client.newCall(request).execute();
看來具體執行是在
Call
裡面
public class Call {
private final OkHttpClient client;
// Guarded by this.
private boolean executed;
volatile boolean canceled;
/** The application's original request unadulterated by redirects or auth headers. */
Request originalRequest;
HttpEngine engine;
原來是
Call
持有了
HttpEngine
,Call是一個待準備執行的請求,可以被取消,由于它代表了單個的請求/回複 對,是以不能被執行兩次。
具體execute的執行:
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
// 結果又交給了OkHttpClient的Dispatcher去執行
client.getDispatcher().executed(this);
Response result = getResponseWithInterceptorChain(false);
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.getDispatcher().finished(this);
}
}
嗯…我們再跳
Dispatcher
:
/** In-flight synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<Call> executedCalls = new ArrayDeque<>();
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(Call call) {
executedCalls.add(call);
}
直接就丢進了隊列裡。坑爹呢?不執行了?!不是叫
executed
嘛?!收拾好日了狗的心情,我們繼續看下去…