1. 程式人生 > >OkHttp 同步異步操作

OkHttp 同步異步操作

過多 com 回調 回復 () 執行 http nfa 透明

OkHttp是一個Java和Android的HTTP和HTTP/2的客戶端,負責發送HTTP請求以及接受HTTP響應。

一、使用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();
        }

  

上面的代碼先創建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());
 
            }
        });

  

同樣是創建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;
    }
}

  上面的代碼中,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();
        }

  

可以看到通過調用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();
        }

  下面是運行結果:

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/}

  

可以發現,攔截器運行了兩次。一次是初始請求”http://www.taobao.com“,一次是請求重定向”https://www.taobao.com“。

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();
      }
    };
  }
}

  

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();
  }
};

  

三、總結

本篇文章主要介紹了OkHttp進行GET的同步、異步請求,對於HTTP其他方法,比如POST等都是可以進行的,這兒就不過多介紹了,想了解的朋友可以到OkHttp Github地址查看.

OkHttp 同步異步操作