Android---網路程式設計之OkHttp3整體結構瞭解以及使用
相關
在這裡為大家提供一種思路,看一款開源專案的時候,可以先看一下,這個開源框架包括那些類,哪些介面,根據註釋大體瞭解一下各個類和介面的作用,這樣有助於我們把握一個專案的全域性架構,更好的理解一個好的開源框架。
OKHttp功能
大家應該都知道介面和抽象類的作用
- 抽象類:用於模板設計,抽離共性,實現多型
- 介面:用於功能的定製
那麼okhttp中的介面都是些什麼,定製了一些什麼樣的功能,可以一起來看一些(基於3.10.0版本)
- Authenticator:響應伺服器的身份驗證,其實現類需要返回一個包含授權頭的請求,也可以返回null,出發這種情況的呼叫者會接受這個響應!
- Call:同步或非同步執行一個請求,該請求能被取消,不能被執行兩次!
- Call.Factory:Call的工廠介面,以前的版本是Call的內部介面!
- Callback:請求失敗或成功的回撥介面,只有兩個方法:失敗:onFailure(Call call, IOException e);成功:onResponse(Call call, Response response)。
- Connection:返回HTTP、HTTPS或HTTPS+HTTP/2連線的套接字和流。連線可以直接指向源伺服器或通過代理。這個類的典型例項是由HTTP客戶端自動建立、連線和執行的。應用程式可以使用這個類來監視作為連線池成員的HTTP連線。
- CookieJar:為HTTP cookie提供策略和永續性。策略(loadForRequest(HttpUrl url)):接受或拒絕cookie。永續性(saveFromResponse(HttpUrl url,List cookies)):儲存cookie,記憶體,檔案,資料庫…
- Dns:把主機名解析為IP地址的域名服務。大多數應用程式將使用預設的系統DNS服務,當然也可以自己實現來使用不同的DNS伺服器,這個介面的實現必須是安全的併發使用。
- EventListener.Factory:EventListener的工廠介面,將在3.11或 3.12版本完善這一系列的API,所有的啟動/連線/獲取事件最終都會收到一個匹配的結束/釋出事件,或者成功的(非空引數),或者失敗(非空的可轉換的)的事件。
- Interceptor:這就是OKHttp至關重要的攔截器介面了,觀察、修改和潛在的短路請求,以及相應的響應。攔截器通常在在請求或響應中新增、刪除或轉換。
- WebSocket:WebSocket的非阻塞式介面,管理web套接字的請求,傳送,佇列訊息位元組大小,取消和關閉。由其工廠類建立物件。
- WebSocket.Factory:WebSocket的工廠介面。
通過以上介面的解析,我們瞭解到了OKHttp的大體功能,依附於這些功能介面,OKHttp是怎樣實現的呢,下面我把OKHttp的類按照義務即他們在整體中擔任的責任劃分出了幾個功能組:
網路客戶端的功能搭建
通過上一篇HTTP的基礎瞭解,我們知道利用HTTP通訊需要設定請求頭,對okhttp來說就是配置物件的屬性,或者通過攔截器處理請求頭和相應訊息。而這些屬性可以有以下類來實現。
- Address:連線到源伺服器的規範。主機名和埠。代理資訊。對於安全連線,地址還包括SSL套接字工廠、主機名驗證器和證書pinner。共享同一地址的HTTP請求也可以共享相同的連線。
- Cache:對快取的管理。
- CacheControl:設定快取策略。
- CacheControl.Builder:構建一個Cache-Control請求頭。builder模式,設定屬性。
- CertificatePinner:構建可信的證書。
- CertificatePinner.Builder:構建一個已配置的證書
- Challenge:配置一個安全的身份驗證
- CipherSuite:TLS密碼元件。
- ConnectionPool:連線池,管理HTTP和HTTP/2的連結以及重用的策略。
- ConnectionSpec:指定在HTTP傳輸時的套接字連線的配置。對於https,這包括在協商安全連線時使用的TLS版本和加密元件。
- ConnectionSpec.Builder:建立ConnectionSpec的物件,並設定其屬性!
- Cookie:cookie管理類
- Cookie.Builder:例項化Cookie物件,但是Cookie的name, value, 和domain values必須在build()方法之前設定。
- Credentials:建立HTTTP授權憑證的工廠
- Dispatcher:執行非同步請求時的策略。
EventListener:事件偵聽器。擴充套件這個類以監視應用程式的HTTP呼叫的數量、大小和持續時間。(非最終API,將在3.11或3.12版本確定)
RFC:是一系列以編號排定的檔案。檔案收集了有關網際網路相關資訊,以及UNIX和網際網路社群的軟體檔案。幾乎所有的因特網標準都收錄在RFC檔案之中。
以上屬性的配置都可以用OkHttpClient,OkHttpClient.Builder配置
- OkHttpClient:實現的介面:Cloneable, Call.Factory, WebSocket.Factory。可以用來發送HTTP請求和讀取響應的呼叫工廠。他適合建立單列,當建立單個OkHttpClient例項並將其用於所有的HTTP呼叫時,OkHttp執行得最好。這是因為每個客戶機都有自己的連線池和執行緒池。重用連線和執行緒可以減少延遲並節省記憶體。相反,為每個請求建立客戶端會浪費空閒池上的資源。OkHttp還使用了HTTP/2連線的守護執行緒。如果它們保持空閒,它們將自動退出。
- OkHttpClient.Builder:建立OkHttpClient,設定OkHttpClient的一些屬性。屬性如下:
屬性 | 型別 | 引用變數 | 設定方法 |
---|---|---|---|
非同步請求時的策略 | Dispatcher | dispatcher | 構造裡面預設初始化;也可以dispatcher(Dispatcher dispatcher) 設定 |
HTTP代理 | Proxy | proxy | proxy(@Nullable Proxy proxy)方法設定 |
客戶端配置的協議集合 | List | protocols | 構造預設為http/1.1和h2的集合;protocols(List protocols)方法設定,目前支援以上兩種協議。 |
套接字配置集合 | List | connectionSpecs | 預設包含一個TLS連結和一個未加密的連結;connectionSpecs(List connectionSpecs)方法設定。 |
配置的攔截器集合 | List | interceptors | addInterceptor(Interceptor interceptor)方法設定攔截器,可以呼叫多次。 |
單個網路請求和響應的攔截器列表 | List | networkInterceptors | addNetworkInterceptor(Interceptor interceptor)配置使用 。 |
事件偵聽器工廠物件 | EventListener.Factory | eventListenerFactory | eventListener(EventListener eventListener)方法轉換設定;eventListenerFactory(EventListener.Factory eventListenerFactory)方法直接設定。 |
設定指定的代理選擇策略 | ProxySelector | proxySelector | 預設使用系統範圍的代理選擇器;proxySelector(ProxySelector proxySelector)方法設定。 |
接收傳入HTTP響應的cookie並提供其處理程式 | CookieJar | cookieJar | 預設為NULL;cookieJar(CookieJar cookieJar) 設定。 |
設定用於讀取和寫入快取響應的響應快取 | Cache;InternalCache | cache;internalCache | cache(@Nullable Cache cache)方法設定。setInternalCache(@Nullable InternalCache internalCache)。(Cache類內部實現了InternalCache介面) |
建立連線的套接字工廠 | SocketFactory | socketFactory | 預設使用系統的套接字工廠;socketFactory(SocketFactory socketFactory)方法設定。 |
安全HTTPS連線的套接字工廠 | SSLSocketFactory | sslSocketFactory | 預設使用系統自帶的;sslSocketFactory(SSLSocketFactory sslSocketFactory)或sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)方法設定,最好使用後者。 |
從Java構建的TLS api中返回的原始陣列計算有效的證書鏈。 | CertificateChainCleaner | certificateChainCleaner | 在設定Https的套接字工廠時被設定。 |
HTTPS連結請求主機名與響應證書的驗證器 | HostnameVerifier | hostnameVerifier | 預設設定OkHostnameVerifier的物件;hostnameVerifier(HostnameVerifier hostnameVerifier)方法設定。 |
約束哪些證書可信任的證書pinner | CertificatePinner | certificatePinner | 構造器中初始化了一個pinne;certificatePinner(CertificatePinner certificatePinner)設定。 |
響應代理伺服器身份驗證器 | Authenticator | proxyAuthenticator | proxyAuthenticator(Authenticator proxyAuthenticator) |
響應源伺服器的身份驗證器。 | Authenticator | authenticator | authenticator(Authenticator authenticator) |
回收HTTP和HTTPS連線的連線池 | ConnectionPool | connectionPool | 預設使用新的連線池;也可以用connectionPool(ConnectionPool connectionPool)方法設定。 |
查詢主機名IP地址的DNS服務 | Dns | dns | 不設定使用一個預設的DNS;用dns(Dns dns)設定自定義dns。 |
例如:下面就是一個基本的客戶端配置
OkHttpClient okHttpClient = new OkHttpClient.Builder()
//快取配置
.cache(new Cache(new File(MainActivity.this.getCacheDir(),"test_cache"),1024*1024*1024))
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
return null;
}
})//設定攔截器 可以對請求和響應做處理,比如快取配置,列印響應流
// .addNetworkInterceptor()同上
.retryOnConnectionFailure(true)//開啟失敗重連
// .authenticator()響應伺服器的身份驗證,可以為NULL
// .certificatePinner() 配置證書
// .connectionPool() 配置連線複用池 , 預設5個 5分鐘
// .connectionSpecs() 連結策略。預設包含一個TLS連結和一個未加密的連結
// .cookieJar() cookie 管理 預設為null
// .dispatcher()非同步請求時的策略 預設已經初始化
// .dns()查詢主機名IP地址的DNS服務 預設使用框架自帶的DNS,也可以自己實現
// .followRedirects() 設定是否可以重定向 預設開啟
// .followSslRedirects()從HTTPS到HTTP和從HTTP到HTTPS的重定向,預設遵循協議重定向
// .hostnameVerifier()HTTPS連結請求主機名與響應證書的驗證器
// .pingInterval()設定此客戶機發起的HTTP/2和web套接字ping之間的間隔。用它來自動傳送ping幀,
// 直到連線失敗或關閉。這使連線存在,並可檢測連線性故障。
// .protocols()客戶端配置的協議集合 預設為http/1.1和h2的集合
// .proxy() HTTP代理
// .readTimeout()設定新連線的預設讀取超時。值0表示沒有超時,否則。當轉換為毫秒時,值必須在1到Integer.MAX_VALUE之間。
// .socketFactory() 預設使用系統的套接字工廠
// .sslSocketFactory() 同上
// .writeTimeout(10, TimeUnit.SECONDS)設定新連線的預設寫入超時。值0表示沒有超時,否則。當轉換為毫秒時,值必須在1到Integer.MAX_VALUE之間。
.build();
基本的屬性已經完成配置,就到了真正發起網路請求了,下面的就屬於請求功能類了
- Request:一個HTTP請求。可以使用get或post方法,可以設定標記取消請求,可以設定此請求的cache-control頭,已替換任何快取控制頭。
- FormBody:繼承RequestBody,表單請求封裝。
- FormBody.Builder:FormBody的內部類,建立FormBody物件並設定屬性!
- Handshake:TLS握手的記錄。這個值物件描述了一個完成的握手。使用ConnectionSpec來設定新握手的策略。
- Headers:單個HTTP訊息的頭欄位。使用請求和對解釋頭的響應。這個類維護HTTP訊息中的頭欄位的順序。
- Headers.Builder:Headers的內部類!建立Headers物件。
- HttpUrl:一個統一資源定位器(URL),帶有http或https的方案。使用這個類來編寫和分解Internet地址。
- HttpUrl.Builde:HttpUrl的內部類,建立HttpUrl物件並設定屬性!
- MediaType:RFC 2045媒體型別,用於描述HTTP請求或響應主體的內容型別。
- MultipartBody:符合RFC 2387(1998年釋出MIME Multipart /相關的內容型別)的請求體。
- MultipartBody.Part:請求頭(Headers)和請求體(RequestBody)的封裝內部類。
- ResponseBody:從源伺服器到客戶端應用程式的使用響應主體原始位元組的一次性流。響應體必須被關閉:每個響應主體由一個有限的資源支援,比如套接字(實時網路響應)或一個開啟的檔案(用於快取的響應)。如果未能關閉響應體,將會洩漏資源,並可能最終導致應用程式卡頓或崩潰。響應體只能使用一次。
- Response:一個HTTP響應。響應體是一次性的。這個類實現Closeable。關閉它只是關閉它的響應主體。
Request request = new Request.Builder()
.url("http://c.3g.163.com/")//可以是string字串,也可以是URL 或是一個HttpUrl
// .addHeader()新增請求頭
// .header() 同上 方式不一樣
// .headers()同上 方式不一樣
// .cacheControl()配置請求時的快取指令
// .delete() 請求時的方法
// .delete(new RequestBody() {
// @Nullable
// @Override
// public MediaType contentType() {
// return null;
// }
//
// @Override
// public void writeTo(BufferedSink sink) throws IOException {
//
// }
// }) 同上
.get() //同上
// .head() //同上
// .method() //同上 自己配置請求方法 和 請求體
// .patch()同上
// .post()同上
// .put()同上
// .removeHeader() 移除請求頭
// .tag() 給此請求配置標記,通過此標記可以取消此請求
.build();
okHttpClient.newCall(request)
// .execute()//同步請求
.enqueue(new Callback() { //非同步請求 有回撥
@Override
public void onFailure(Call call, IOException e) {
//請求失敗回撥方法
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//請求成功回掉方法
}
});
Route:連線到抽象源伺服器的具體路由。在建立連線時,可以有多個選擇:
1.HTTP代理:可以為客戶端顯式地配置代理伺服器。否則將使用代理選擇器。它可以返回多個代理來嘗試。
2.IP地址:是否直接連線到源伺服器或代理,開啟套接字需要一個IP地址。DNS伺服器可能會返回多個IP地址來嘗試。
每個路徑都是這些選項的特定選擇。WebSocketListener:WebSocket連線時的一些監聽。
至此,我們結合框架中的類和功能介面,對okhttp框架的使用完整的梳理了一遍,這樣我也有了深刻的印象,當我們需要怎樣配置就可以怎樣配置了。一個簡單的網路請求(例子)。
//快取目錄
cache = new Cache(new File(this.getCacheDir(),"test_cache"),1024*1024*1024);
//配置快取策略
cacheInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//request = request.newBuilder().header("Cache-Control","public, max-age=3600").build(); 可以在這個地方配置 ,也可以在下面request配置
if(!isNetworkAvailable(MainActivity.this)){
request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
}
Response response = chain.proceed(request);
String cacheControl = request.cacheControl().toString();
if(isNetworkAvailable(MainActivity.this)){
return response.newBuilder()
.header("Cache-Control",cacheControl)
.header("User-Agent","oktest")
.removeHeader("Pragma")
.build();
}else {
return response.newBuilder()
.header("Cache-Control","public, " + CACHE_CONTROL_CACHE)
.header("User-Agent","oktest")
.removeHeader("Pragma")
.build();
}
}
};
//列印請求頭 url;列印響應頭 響應時間 響應體
logInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
Log.e("Sending request",request.url().toString());
Log.e("request connection",chain.connection()+"");
Log.e("request headers",request.headers().toString());
Response response = chain.proceed(request);
long t2 = System.nanoTime();
Log.e("Received response for",response.request().url().toString());
Log.e("response time",(t2 - t1) / 1e6d+"毫秒");
Log.e("response headers",response.headers().toString());
Log.e("response body",response.body().toString());
//在這裡也可以對response.body()操作處理
return response;
}
};
OkHttpClient okHttpClient = new OkHttpClient.Builder()
//快取配置
.cache(cache)
.addInterceptor(cacheInterceptor)//設定攔截器 可以對請求和響應做處理,比如快取配置,列印響應流
.addNetworkInterceptor(cacheInterceptor)//同上
.addInterceptor(logInterceptor)
.retryOnConnectionFailure(true)//開啟失敗重連
.build();
Request request = new Request.Builder()
.url("https://blog.csdn.net/zcpHappy/article/details/79719141")//可以是string字串,也可以是URL 或是一個HttpUrl
.header("User-Agent", "oktest") //同上 方式不一樣
.get() //同上
.build();
okHttpClient.newCall(request)
// .execute()//同步請求
.enqueue(new Callback() { //非同步請求 有回撥
@Override
public void onFailure(Call call, IOException e) {
//請求失敗回撥方法
tvShow.setText(e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//請求成功回掉方法
// String responseString = response.body().string();
InputStream inputStream = response.body().byteStream();
final StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line;
try{
while ((line = bufferedReader.readLine())!=null){
stringBuilder.append(line+"\n");
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
inputStream.close();
}catch (Exception eInput){
eInput.printStackTrace();
}
}
runOnUiThread(new Runnable() {
@Override
public void run() {
tvShow.setText(stringBuilder.toString());
}
});
}
});
下面是有網路的情況下 請求頭 響應頭 輸出情況
04-12 13:18:11.649 5065-5156/com.example.zcp.ok3test E/Sending request: https://blog.csdn.net/zcpHappy/article/details/79719141
04-12 13:18:11.649 5065-5156/com.example.zcp.ok3test E/request connection: null
04-12 13:18:11.649 5065-5156/com.example.zcp.ok3test E/request headers: User-Agent: oktest
Cache-Control: public, max-age=3600
04-12 13:18:12.299 5065-5156/com.example.zcp.ok3test E/Received response for: https://blog.csdn.net/zcpHappy/article/details/79719141
04-12 13:18:12.299 5065-5156/com.example.zcp.ok3test E/response time: 651.625836毫秒
04-12 13:18:12.299 5065-5156/com.example.zcp.ok3test E/response headers: Server: openresty
Date: Thu, 12 Apr 2018 05:18:12 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=20
Set-Cookie: uuid_tt_dd=10_9767942580-1523510292191-844897; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
Set-Cookie: dc_session_id=10_1523510292191.777942; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
Vary: Accept-Encoding
Strict-Transport-Security: max-age= 31536000
Cache-Control: public, max-age=3600
User-Agent: oktest
04-12 13:18:12.299 5065-5156/com.example.zcp.ok3test E/response body: okhttp3.internal.http.RealResponseBody@4a7dcc80
有網路響應體截圖:
這部分是無網路情況下使用快取,請求頭 響應頭輸出情況
04-12 13:25:13.174 5065-11442/com.example.zcp.ok3test E/Sending request: https://blog.csdn.net/zcpHappy/article/details/79719141
04-12 13:25:13.174 5065-11442/com.example.zcp.ok3test E/request connection: null
04-12 13:25:13.174 5065-11442/com.example.zcp.ok3test E/request headers: User-Agent: oktest
Cache-Control: public, max-age=3600
04-12 13:25:13.174 5065-11442/com.example.zcp.ok3test E/Received response for: https://blog.csdn.net/zcpHappy/article/details/79719141
04-12 13:25:13.174 5065-11442/com.example.zcp.ok3test E/response time: 2.990045毫秒
04-12 13:25:13.174 5065-11442/com.example.zcp.ok3test E/response headers: Server: openresty
Date: Thu, 12 Apr 2018 05:18:12 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=20
Set-Cookie: uuid_tt_dd=10_9767942580-1523510292191-844897; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
Set-Cookie: dc_session_id=10_1523510292191.777942; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
Vary: Accept-Encoding
Strict-Transport-Security: max-age= 31536000
Cache-Control: public, max-age=3600
User-Agent: oktest
04-12 13:25:13.174 5065-11442/com.example.zcp.ok3test E/response body: okhttp3.internal.http.RealResponseBody@4a854060
無網路,快取中的響應輸出
可以看到,配置快取以後 請求到響應從651.625836毫秒下降到2.990045毫秒;當然配置快取以後,在有網路的情況下也是使用的快取