okhttp 同步請求和非同步請求
一、使用OkHttp
OkHttp傳送請求後,可以通過同步或非同步地方式獲取響應。下面就同步和非同步兩種方式進行介紹。
1.1、同步方式
傳送請求後,就會進入阻塞狀態,知道收到響應。下面看一個下載百度首頁的例子:
OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
Request request = new Request.Builder().url("http://www.baidu.com")
.get().build ();
Call call = client.newCall(request);
try {
Response response = call.execute();
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
上面的程式碼先建立OkHttpClient和Request物件,兩者均使用了Builder模式;然後將Request封裝成Call物件,然後呼叫Call的execute()同步傳送請求,最後列印響應。
1.2、非同步方式
非同步方式是在回撥中處理響應的,同樣看下載百度首頁的例子:
OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
Request request = new Request.Builder().url("http://www.baidu.com")
.get().build();
Call call = client.newCall(request);
call.enqueue (new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.out.println("Fail");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println(response.body().string());
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
同樣是建立OkHttpClient、Request和Call,只是呼叫了enqueue方法並在回撥中處理響應。
上面介紹了同步、非同步獲取請求的步驟,都是比較簡單的。
1.3、Request、Response、Call
上面的程式碼中涉及到幾個常用的類:Request、Response和Call。下面分別介紹:
Request
每一個HTTP請求包含一個URL、一個方法(GET或POST或其他)、一些HTTP頭。請求還可能包含一個特定內容型別的資料類的主體部分。
Response
響應是對請求的回覆,包含狀態碼、HTTP頭和主體部分。
重寫請求
當將Request提交給OkHttp後,出於正確性和效率的考慮,OkHttp在傳輸請求之前會重寫請求。
OkHttp可能會在請求中新增缺少的請求頭,包括”Content-Length”,”Transfer-Encoding”,”User-Agent”,”HOST”,”Connection”和”Content-Type”等。
有些請求可能有快取的響應。當快取響應過時時,OkHttp可以做一個額外的GET請求獲取最新的響應。這要求”If-Modified-Since”和”If-None-Match”頭被新增。
重寫響應
如果使用了透明壓縮,OkHttp會丟棄”Content-Encoding”和”Content-Length”頭,因為和解壓後的響應主體不匹配。
如果一個額外的GET請求成功了,那麼網路和快取中的響應將會合並。
請求重定向
當請求的URL移動了,web伺服器會返回一個302的狀態碼並指明檔案的新地址。OkHttp將會重定向獲取最終的響應。
請求重試
有時連線會失敗,那麼OkHttp會重試別的路由。
Call
當重寫、重定向等時,一個請求可能會產生多個請求和響應。OkHttp使用Call抽象出一個滿足請求的模型,儘管中間可能會有多個請求或響應。執行Call有兩種方式,同步或非同步,這在上面已經介紹過了。
Call可以在任何執行緒被取消。
二、攔截器
攔截器是一個監視、重寫、重試請求的強有力機制。攔截器可以串聯。
從圖中可以看出,攔截器分為應用攔截器和網路攔截器兩種。應用攔截器是在傳送請求之前和獲取到響應之後進行操作的,網路攔截器是在進行網路獲取前進行操作的。
2.1、應用攔截器
下面定義一個應用攔截器,用於在請求傳送前列印URL以及接受到響應後列印內容。
public class LogInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
System.out.println(request.toString());
Response response = chain.proceed(request);
System.out.println(response);
return response;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
上面的程式碼中,LogInterceptor實現了Interceptor介面。首先從chain中得到請求,然後列印請求;然後呼叫proceed方法處理請求得到響應,然後列印響應。呼叫程式碼如下:
OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new LogInterceptor()).build();
Request request = new Request.Builder().url("http://www.baidu.com")
.get().build();
Call call = okHttpClient.newCall(request);
try {
call.execute();
} catch (IOException e) {
e.printStackTrace();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
可以看到通過呼叫addInterceptor方法新增應用攔截器。
2.2、網路攔截器
網路攔截器的使用和應用攔截器類似,只是呼叫OkHttpClient的addNetworkInterceptor方法即可。
OkHttpClient okHttpClient = new OkHttpClient.Builder().addNetworkInterceptor(new LogInterceptor()).build();
Request request = new Request.Builder().url("http://www.taobao.com")
.get().build();
Call call = okHttpClient.newCall(request);
try {
call.execute();
} catch (IOException e) {
e.printStackTrace();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
下面是執行結果:
Request{method=GET, url=http://www.taobao.com/, tag=Request{method=GET, url=http://www.taobao.com/, tag=null}}
Response{protocol=http/1.1, code=302, message=Found, url=http://www.taobao.com/}
Request{method=GET, url=https://www.taobao.com/, tag=Request{method=GET, url=http://www.taobao.com/, tag=null}}
Response{protocol=http/1.1, code=200, message=OK, url=https://www.taobao.com/}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
2.3、應用攔截器和網路攔截器的比較
每個攔截器由它各自的優勢。
應用攔截器
- 不需要考慮中間狀態的響應,比如重定向或者重試。
- 只會被呼叫一次,甚至於HTTP響應儲存在快取中。
- 觀察應用程式的原意。
- 允許短路,可以不呼叫Chain.proceed()方法
- 允許重試和傳送多條請求,呼叫Chain.proceed()方法
網路攔截器
- 可以操作中間狀態的響應,比如重定向和重試
- 不呼叫快取的響應
- 可以觀察整個網路上傳輸的資料
- 獲得攜帶請求的Connection
2.4、重寫請求
攔截器可以新增、移除或者替換請求的頭資訊,也可以改變傳輸的主體部分。下面的一個攔截器對請求主體進行Gzip壓縮。
final class GzipRequestInterceptor implements Interceptor {
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request originalRequest = chain.request();
if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {
return chain.proceed(originalRequest);
}
Request compressedRequest = originalRequest.newBuilder()
.header("Content-Encoding", "gzip")
.method(originalRequest.method(), gzip(originalRequest.body()))
.build();
return chain.proceed(compressedRequest);
}
private RequestBody gzip(final RequestBody body) {
return new RequestBody() {
@Override public MediaType contentType() {
return body.contentType();
}
@Override public long contentLength() {
return -1; // We don't know the compressed length in advance!
}
@Override public void writeTo(BufferedSink sink) throws IOException {
BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
body.writeTo(gzipSink);
gzipSink.close();
}
};
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
2.5、重寫響應
同樣地,攔截器可以重寫響應的頭部以及主體部分。但是
/** Dangerous interceptor that rewrites the server's cache-control header. */
private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.header("Cache-Control", "max-age=60")
.build();
}
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
備註:
備註:
-
同步互動:指傳送一個請求,需要等待返回,然後才能夠傳送下一個請求,有個等待過程;
-
非同步互動:指傳送一個請求,不需要等待返回,隨時可以再發送下一個請求,即不需要等待。
-
區別:一個需要等待,一個不需要等待,在部分情況下,我們的專案開發中都會優先選擇不需要等待的非同步互動方式。
-
哪些情況建議使用同步互動呢?比如銀行的轉賬系統,對資料庫的儲存操作等等,都會使用同步互動操作,其餘情況都優先使用非同步互動。
三、總結
本篇文章主要介紹了OkHttp進行GET的同步、非同步請求,對於HTTP其他方法,比如POST等都是可以進行的,這兒就不過多介紹了,想了解的朋友可以到OkHttp Github地址檢視.