一、OKHTTP簡介
OKHttp是一個處理網絡請求的開源項目OKHttp GitHub地址
OKHttp優點
- 支持HTTP2/SPDY(SPDY是Google開發的基於TCP的傳輸層協議,用以最小化網絡延遲,提升網絡速度,優化用戶的網絡使用體驗。)
- socket自動選擇最好路線,並支持自動重連,擁有自動維護的socket連接池,減少握手次數,減少了請求延遲,共享Socket,減少對服務器的請求次數。
- 基於Headers的緩存策略減少重複的網絡請求。
- 擁有Interceptors輕鬆處理請求與響應(自動處理GZip壓縮)。
OKHttp的功能
- PUT,DELETE,POST,GET等請求
- 文件的上傳下載
- 加載圖片(內部會圖片大小自動壓縮)
- 支持請求回調,直接返回對象、對象集合
- 支持session的保持
二、OkHttp使用
此處只舉例異步調用:
//1.創建OkHttpClient對象
OkHttpClient okHttpClient = new OkHttpClient();
//2.創建Request對象,設置一個url地址(百度地址),設置請求方式。
Request request = new Request.Builder().url("http://www.baidu.com").method("GET",null).build();
//3.創建一個call對象,參數就是Request請求對象
Call call = okHttpClient.newCall(request);
//4.請求加入調度,重寫回調方法
call.enqueue(new Callback() {
//請求失敗執行的方法
@Override
public void onFailure(Call call, IOException e) {
}
//請求成功執行的方法
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
上面就是發送一個異步GET請求的4個步驟:
- 創建OkHttpClient對象
- 通過Builder模式創建Request對象,參數必須有個url參數,可以通過Request.Builder設置更多的參數比如:header、method等
- 通過request的對象去構造得到一個Call對象,Call對象有execute()和cancel()等方法。
- 以異步的方式去執行請求,調用的是call.enqueue,將call加入調度隊列,任務執行完成會在Callback中得到結果。
注意事項:
- 異步調用的回調函數是在子線程,我們不能在子線程更新UI,需要藉助於 runOnUiThread() 方法或者 Handler 來處理。
- onResponse回調有一個參數是response,如果我們想獲得返回的是字符串,可以通過response.body().string()獲取;如果希望獲得返回的二進制字節數組,則調用response.body().bytes();如果你想拿到返回的inputStream,則調response.body().byteStream(),有inputStream我們就可以通過IO的方式寫文件
三、OkHttpClient源碼分析
1、先看內部類和構造函數的實現
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
//協議,支持http2和http1
static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
Protocol.HTTP_2, Protocol.HTTP_1_1);
//連接規範,帶有擴展的現代TLS連接 和 未加密、未經身份驗證的連接
//具體作用先不看
static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);
static {
Internal.instance = new Internal() {
//加入請求頭
@Override public void addLenient(Headers.Builder builder, String line) {
builder.addLenient(line);
}
//加入請求頭
@Override public void addLenient(Headers.Builder builder, String name, String value) {
builder.addLenient(name, value);
}
//設置緩存
@Override public void setCache(OkHttpClient.Builder builder, InternalCache internalCache) {
builder.setInternalCache(internalCache);
}
//連接置空閒,兩個參數ConnectionPool(連接池),RealConnection(真實連接)
//在連接池把真實連接置爲空閒狀態
@Override public boolean connectionBecameIdle(
ConnectionPool pool, RealConnection connection) {
return pool.connectionBecameIdle(connection);
}
//獲取真實連接,通過address(地址)、StreamAllocation(流分配)、Route (線路)
//在連接池獲取當前真實連接
@Override public RealConnection get(ConnectionPool pool, Address address,
StreamAllocation streamAllocation, Route route) {
return pool.get(address, streamAllocation, route);
}
//判斷兩個主機是否相同
@Override public boolean equalsNonHost(Address a, Address b) {
return a.equalsNonHost(b);
}
//消除重複連接
@Override public Socket deduplicate(
ConnectionPool pool, Address address, StreamAllocation streamAllocation) {
return pool.deduplicate(address, streamAllocation);
}
//加入連接池
@Override public void put(ConnectionPool pool, RealConnection connection) {
pool.put(connection);
}
//線路集合
@Override public RouteDatabase routeDatabase(ConnectionPool connectionPool) {
return connectionPool.routeDatabase;
}
//響應狀態碼
@Override public int code(Response.Builder responseBuilder) {
return responseBuilder.code;
}
//
@Override
public void apply(ConnectionSpec tlsConfiguration, SSLSocket sslSocket, boolean isFallback) {
tlsConfiguration.apply(sslSocket, isFallback);
}
//無效地址
@Override public boolean isInvalidHttpUrlHost(IllegalArgumentException e) {
return e.getMessage().startsWith(HttpUrl.Builder.INVALID_HOST);
}
//流分配
@Override public StreamAllocation streamAllocation(Call call) {
return ((RealCall) call).streamAllocation();
}
//異常
@Override public @Nullable IOException timeoutExit(Call call, @Nullable IOException e) {
return ((RealCall) call).timeoutExit(e);
}
//創建新的call
@Override public Call newWebSocketCall(OkHttpClient client, Request originalRequest) {
return RealCall.newRealCall(client, originalRequest, true);
}
};
}
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;//OKhttp內部緩存
final SocketFactory socketFactory;//創建socket的工廠
final SSLSocketFactory sslSocketFactory;//ssl的socket工廠繼承於SocketFactory
final CertificateChainCleaner certificateChainCleaner;//證書的獲取和清除
final HostnameVerifier hostnameVerifier;//主機認證
final CertificatePinner certificatePinner;//該類用於約束哪些證書是可信的。 鎖定證書可以防止對證書頒發機構相關的攻擊。 它還阻止通過用戶已知或未知的中間證書頒發機構建立的連接
final Authenticator proxyAuthenticator;//認證器
final Authenticator authenticator;//
final ConnectionPool connectionPool;//連接池
final Dns dns;//dns獲取ip,OKHTTP嘗試的順序排列。如果與地址的連接失敗,OKHTTP將重試與下一個地址的連接,直到要麼建立連接,要麼耗盡IP地址集,要麼超過限制
final boolean followSslRedirects;//遵循SSL重定向
final boolean followRedirects;//遵循重定向
final boolean retryOnConnectionFailure;//重試連接失敗
final int callTimeout;//請求超時
final int connectTimeout;//連接超時
final int readTimeout;//讀超時
final int writeTimeout;//寫超時
final int pingInterval;//ping 間隔時長
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);
}
}