OkHttp使用教程

Android系統提供了兩種HTTP通信類,HttpURLConnection和HttpClient。
關於HttpURLConnection和HttpClient的選擇>>官方博客
儘管Google在大部分安卓版本中推薦使用HttpURLConnection,但是這個類相比HttpClient實在是太難用,太弱爆了。
OkHttp是一個相對成熟的解決方案,據說Android4.4的源碼中可以看到HttpURLConnection已經替換成OkHttp實現了。所以我們更有理由相信OkHttp的強大。

OkHttp 處理了很多網絡疑難雜症:會從很多常用的連接問題中自動恢復。如果您的服務器配置了多個IP地址,當第一個IP連接失敗的時候,OkHttp會自動嘗試下一個IP。OkHttp還處理了代理服務器問題和SSL握手失敗問題。

使用 OkHttp 無需重寫您程序中的網絡代碼。OkHttp實現了幾乎和java.net.HttpURLConnection一樣的API。如果你用了 Apache HttpClient,則OkHttp也提供了一個對應的okhttp-apache 模塊。


注:在國內使用OkHttp會因爲這個問題導致部分酷派手機用戶無法聯網,所以對於大衆app來說,需要等待這個bug修復後再使用。或者嘗試使用OkHttp的老版本。
截止到目前,OkHttp一直沒有修復,並把修復計劃延遲到了OkHttp2.3中。不是所有設備都能重現,僅少量設備會出現這個問題。(如果問題這麼明顯,OkHttp早就修復了)

入門

官方資料

官方介紹
github源碼

使用範圍

OkHttp支持Android 2.3及其以上版本。
對於Java, JDK1.7以上。

jar包準備

官方介紹頁面有鏈接位置。這裏把下載鏈接也寫在下面。
OkHttp
Okio

基本使用

HTTP GET

1
2
3
4
5
6
7
8
OkHttpClient client = new OkHttpClient();
 
String run(String url) throws IOException {
    Request request = new Request.Builder().url(url).build();
    Response response = client.newCall(request).execute();    if (response.isSuccessful()) {        return response.body().string();
    else {        throw new IOException("Unexpected code " + response);
    }
}

Request是OkHttp中訪問的請求,Builder是輔助類。Response即OkHttp中的響應。

Response類:

1
2
3
public boolean isSuccessful()
Returns true if the code is in [200..300),
 which means the request was successfully received, understood, and accepted.

response.body()返回ResponseBody類

可以方便的獲取string

1
2
3
4
public final String string() throws IOException
Returns the response as a string decoded with the charset of the Content-Type header. If that header is either absent or lacks a charset,
 this will attempt to decode the response body as UTF-8.Throws:
IOException

當然也能獲取到流的形式:

1
public final InputStream byteStream()

HTTP POST

POST提交Json數據

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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();
    f (response.isSuccessful()) {
        return response.body().string();
    else {
        throw new IOException("Unexpected code " + response);
    }
}

使用Request的post方法來提交請求體RequestBody

POST提交鍵值對

很多時候我們會需要通過POST方式把鍵值對數據傳送到服務器。 OkHttp提供了很方便的方式來做這件事情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
 
     RequestBody formBody = new FormEncodingBuilder()
    .add("platform""android")
    .add("name""bug")
    .add("subject""XXXXXXXXXXXXXXX")
    .build();
 
      Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
 
      Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        return response.body().string();
    else {
        throw new IOException("Unexpected code " + response);
    }
}

總結

通過上面的例子我們可以發現,OkHttp在很多時候使用都是很方便的,而且很多代碼也有重複,因此特地整理了下面的工具類。
注意:

  • OkHttp官方文檔並不建議我們創建多個OkHttpClient,因此全局使用一個。 如果有需要,可以使用clone方法,再進行自定義。這點在後面的高級教程裏會提到。

  • enqueue爲OkHttp提供的異步方法,入門教程中並沒有提到,後面的高級教程裏會有解釋。

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;
import cn.wiz.sdk.constant.WizConstant;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response; 
  
public class OkHttpUtil {
    private static final OkHttpClient mOkHttpClient = new OkHttpClient();
    static{
        mOkHttpClient.setConnectTimeout(30, TimeUnit.SECONDS);
    }
    /**
     * 該不會開啓異步線程。
     * @param request
     * @return
     * @throws IOException
     */
    public static Response execute(Request request) throws IOException{
        return mOkHttpClient.newCall(request).execute();
    }
    /**
     * 開啓異步線程訪問網絡
     * @param request
     * @param responseCallback
     */
    public static void enqueue(Request request, Callback responseCallback){
        mOkHttpClient.newCall(request).enqueue(responseCallback);
    }
    /**
     * 開啓異步線程訪問網絡, 且不在意返回結果(實現空callback)
     * @param request
     */
    public static void enqueue(Request request){
        mOkHttpClient.newCall(request).enqueue(new Callback() {
             
            @Override
            public void onResponse(Response arg0) throws IOException {
                 
            }
             
            @Override
            public void onFailure(Request arg0, IOException arg1) {
                 
            }
        });
    }
    public static String getStringFromServer(String url) throws IOException{
        Request request = new Request.Builder().url(url).build();
        Response response = execute(request);
        if (response.isSuccessful()) {
            String responseUrl = response.body().string();
            return responseUrl;
        else {
            throw new IOException("Unexpected code " + response);
        }
    }
    private static final String CHARSET_NAME = "UTF-8";
    /**
     * 這裏使用了HttpClinet的API。只是爲了方便
     * @param params
     * @return
     */
    public static String formatParams(List<BasicNameValuePair> params){
        return URLEncodedUtils.format(params, CHARSET_NAME);
    }
    /**
     * 爲HttpGet 的 url 方便的添加多個name value 參數。
     * @param url
     * @param params
     * @return
     */
    public static String attachHttpGetParams(String url, List<BasicNameValuePair> params){
        return url + "?" + formatParams(params);
    }
    /**
     * 爲HttpGet 的 url 方便的添加1個name value 參數。
     * @param url
     * @param name
     * @param value
     * @return
     */
    public static String attachHttpGetParam(String url, String name, String value){
        return url + "?" + name + "=" + value;
    }
}

高級

高級屬性其實用的不多,這裏主要是對OkHttp github官方教程進行了翻譯。

同步get

下載一個文件,打印他的響應頭,以string形式打印響應體。
響應體的 string() 方法對於小文檔來說十分方便、高效。但是如果響應體太大(超過1MB),應避免適應 string()方法 ,因爲他會將把整個文檔加載到內存中。
對於超過1MB的響應body,應使用流的方式來處理body。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private final OkHttpClient client = new OkHttpClient();
 
public void run() throws Exception {
    Request request = new Request.Builder()
        .build();
 
    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
 
    Headers responseHeaders = response.headers();
    for (int i = 0; i < responseHeaders.size(); i++) {
      System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
    }
 
    System.out.println(response.body().string());
}

異步get

在一個工作線程中下載文件,當響應可讀時回調Callback接口。讀取響應時會阻塞當前線程。OkHttp現階段不提供異步api來接收響應體。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private final OkHttpClient client = new OkHttpClient();
 
public void run() throws Exception {
    Request request = new Request.Builder()
        .build();
 
    client.newCall(request).enqueue(new Callback() {
      @Override public void onFailure(Request request, Throwable throwable) {
        throwable.printStackTrace();
      }
 
      @Override public void onResponse(Response response) throws IOException {
        if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
 
        Headers responseHeaders = response.headers();
        for (int i = 0; i < responseHeaders.size(); i++) {
          System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
        }
 
        System.out.println(response.body().string());
      }
    });
}

提取響應頭

典型的HTTP頭 像是一個 Map<String, String> :每個字段都有一個或沒有值。但是一些頭允許多個值,像Guava的Multimap。例如:HTTP響應裏面提供的Vary響應頭,就是多值的。OkHttp的api試圖讓這些情況都適用。
當寫請求頭的時候,使用header(name, value)可以設置唯一的name、value。如果已經有值,舊的將被移除,然後添加新的。使用addHeader(name, value)可以添加多值(添加,不移除已有的)。
當讀取響應頭時,使用header(name)返回最後出現的name、value。通常情況這也是唯一的name、value。如果沒有值,那麼header(name)將返回null。如果想讀取字段對應的所有值,使用headers(name)會返回一個list。
爲了獲取所有的Header,Headers類支持按index訪問。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private final OkHttpClient client = new OkHttpClient();
 
public void run() throws Exception {
    Request request = new Request.Builder()
        .header("User-Agent""OkHttp Headers.java")
        .addHeader("Accept""application/json; q=0.5")
        .addHeader("Accept""application/vnd.github.v3+json")
        .build();
 
    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
 
    System.out.println("Server: " + response.header("Server"));
    System.out.println("Date: " + response.header("Date"));
    System.out.println("Vary: " + response.headers("Vary"));
}

Post方式提交String

使用HTTP POST提交請求到服務。這個例子提交了一個markdown文檔到web服務,以HTML方式渲染markdown。因爲整個請求體都在內存中,因此避免使用此api提交大文檔(大於1MB)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static final MediaType MEDIA_TYPE_MARKDOWN
  = MediaType.parse("text/x-markdown; charset=utf-8");
 
private final OkHttpClient client = new OkHttpClient();
 
public void run() throws Exception {
    String postBody = ""
        "Releases\n"
        "--------\n"
        "\n"
        " * _1.0_ May 6, 2013\n"
        " * _1.1_ June 15, 2013\n"
        " * _1.2_ August 11, 2013\n";
 
    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
        .build();
 
    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
 
    System.out.println(response.body().string());
}

Post方式提交流

以流的方式POST提交請求體。請求體的內容由流寫入產生。這個例子是流直接寫入Okio的BufferedSink。你的程序可能會使用OutputStream,你可以使用BufferedSink.outputStream()來獲取。

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
33
34
35
36
37
38
public static final MediaType MEDIA_TYPE_MARKDOWN
      = MediaType.parse("text/x-markdown; charset=utf-8");
 
private final OkHttpClient client = new OkHttpClient();
 
public void run() throws Exception {
    RequestBody requestBody = new RequestBody() {
      @Override public MediaType contentType() {
        return MEDIA_TYPE_MARKDOWN;
      }
 
      @Override public void writeTo(BufferedSink sink) throws IOException {
        sink.writeUtf8("Numbers\n");
        sink.writeUtf8("-------\n");
        for (int i = 2; i <= 997; i++) {
          sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
        }
      }
 
      private String factor(int n) {
        for (int i = 2; i < n; i++) {
          int x = n / i;
          if (x * i == n) return factor(x) + " × " + i;
        }
        return Integer.toString(n);
      }
    };
 
    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(requestBody)
        .build();
 
    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
 
    System.out.println(response.body().string());
}

Post方式提交文件

以文件作爲請求體是十分簡單的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static final MediaType MEDIA_TYPE_MARKDOWN
  = MediaType.parse("text/x-markdown; charset=utf-8");
 
private final OkHttpClient client = new OkHttpClient();
 
public void run() throws Exception {
    File file = new File("README.md");
 
    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
        .build();
 
    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
 
    System.out.println(response.body().string());
}

Post方式提交表單

使用FormEncodingBuilder來構建和HTML<form>標籤相同效果的請求體。鍵值對將使用一種HTML兼容形式的URL編碼來進行編碼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private final OkHttpClient client = new OkHttpClient();
 
public void run() throws Exception {
    RequestBody formBody = new FormEncodingBuilder()
        .add("search""Jurassic Park")
        .build();
    Request request = new Request.Builder()
        .url("https://en.wikipedia.org/w/index.php")
        .post(formBody)
        .build();
 
    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
 
    System.out.println(response.body().string());
}

Post方式提交分塊請求

MultipartBuilder可以構建複雜的請求體,與HTML文件上傳形式兼容。多塊請求體中每塊請求都是一個請求體,可以定義自己的請求頭。這些請求頭可以用來描述這塊請求,例如他的Content-Disposition。如果Content-LengthContent-Type可用的話,他們會被自動添加到請求頭中。

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
private static final String IMGUR_CLIENT_ID = "...";
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
 
private final OkHttpClient client = new OkHttpClient();
 
public void run() throws Exception {
    // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
    RequestBody requestBody = new MultipartBuilder()
        .type(MultipartBuilder.FORM)
        .addPart(
            Headers.of("Content-Disposition""form-data; name=\"title\""),
            RequestBody.create(null"Square Logo"))
        .addPart(
            Headers.of("Content-Disposition""form-data; name=\"image\""),
            RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
        .build();
 
    Request request = new Request.Builder()
        .header("Authorization""Client-ID " + IMGUR_CLIENT_ID)
        .url("https://api.imgur.com/3/image")
        .post(requestBody)
        .build();
 
    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
 
    System.out.println(response.body().string());
}

使用Gson來解析JSON響應

Gson是一個在JSON和Java對象之間轉換非常方便的api。這裏我們用Gson來解析Github API的JSON響應。
注意:ResponseBody.charStream()使用響應頭Content-Type指定的字符集來解析響應體。默認是UTF-8。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private final OkHttpClient client = new OkHttpClient();
private final Gson gson = new Gson();
 
public void run() throws Exception {
    Request request = new Request.Builder()
        .build();
    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
 
    Gist gist = gson.fromJson(response.body().charStream(), Gist.class);
    for (Map.Entry<String, GistFile> entry : gist.files.entrySet()) {
      System.out.println(entry.getKey());
      System.out.println(entry.getValue().content);
    }
}
 
static class Gist {
    Map<String, GistFile> files;
}
 
static class GistFile {
    String content;
}

響應緩存

爲了緩存響應,你需要一個你可以讀寫的緩存目錄,和緩存大小的限制。這個緩存目錄應該是私有的,不信任的程序應不能讀取緩存內容。
一個緩存目錄同時擁有多個緩存訪問是錯誤的。大多數程序只需要調用一次new OkHttp(),在第一次調用時配置好緩存,然後其他地方只需要調用這個實例就可以了。否則兩個緩存示例互相干擾,破壞響應緩存,而且有可能會導致程序崩潰。
響應緩存使用HTTP頭作爲配置。你可以在請求頭中添加Cache-Control: max-stale=3600 ,OkHttp緩存會支持。你的服務通過響應頭確定響應緩存多長時間,例如使用Cache-Control: max-age=9600

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
33
private final OkHttpClient client;
 
public CacheResponse(File cacheDirectory) throws Exception {
    int cacheSize = 10 * 1024 * 1024; // 10 MiB
    Cache cache = new Cache(cacheDirectory, cacheSize);
 
    client = new OkHttpClient();
    client.setCache(cache);
}
 
public void run() throws Exception {
    Request request = new Request.Builder()
        .build();
 
    Response response1 = client.newCall(request).execute();
    if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);
 
    String response1Body = response1.body().string();
    System.out.println("Response 1 response:          " + response1);
    System.out.println("Response 1 cache response:    " + response1.cacheResponse());
    System.out.println("Response 1 network response:  " + response1.networkResponse());
 
    Response response2 = client.newCall(request).execute();
    if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);
 
    String response2Body = response2.body().string();
    System.out.println("Response 2 response:          " + response2);
    System.out.println("Response 2 cache response:    " + response2.cacheResponse());
    System.out.println("Response 2 network response:  " + response2.networkResponse());
 
    System.out.println("Response 2 equals Response 1? " + response1Body.equals(response2Body));
}

擴展

在這一節還提到了下面一句:
There are cache headers to force a cached response, force a network response, or force the network response to be validated with a conditional GET.

我不是很懂cache,平時用到的也不多,所以把Google在Android Developers一段相關的解析放到這裏吧。

Force a Network Response

In some situations, such as after a user clicks a 'refresh' button, it may be necessary to skip the cache, and fetch data directly from the server. To force a full refresh, add the no-cache directive:

[java] view plaincopy
  1. connection.addRequestProperty("Cache-Control""no-cache");  

If it is only necessary to force a cached response to be validated by the server, use the more efficient max-age=0 instead:

[java] view plaincopy
  1. connection.addRequestProperty("Cache-Control""max-age=0");  

Force a Cache Response

Sometimes you'll want to show resources if they are available immediately, but not otherwise. This can be used so your application can show something while waiting for the latest data to be downloaded. To restrict a request to locally-cached resources, add the only-if-cached directive:

1
2
3
4
5
6
7
8
try {
     connection.addRequestProperty("Cache-Control""only-if-cached");
     InputStream cached = connection.getInputStream();
     // the resource was cached! show it
  catch (FileNotFoundException e) {
     // the resource was not cached
 }
}

This technique works even better in situations where a stale response is better than no response. To permit stale cached responses, use the max-stale directive with the maximum staleness in seconds:

1
int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks staleconnection.addRequestProperty("Cache-Control", "max-stale=" + maxStale);

以上信息來自:HttpResponseCache - Android SDK | Android Developers

取消一個Call

使用Call.cancel()可以立即停止掉一個正在執行的call。如果一個線程正在寫請求或者讀響應,將會引發IOException。當call沒有必要的時候,使用這個api可以節約網絡資源。例如當用戶離開一個應用時。不管同步還是異步的call都可以取消。
你可以通過tags來同時取消多個請求。當你構建一請求時,使用RequestBuilder.tag(tag)來分配一個標籤。之後你就可以用OkHttpClient.cancel(tag)來取消所有帶有這個tag的call。

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
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
private final OkHttpClient client = new OkHttpClient();
 
public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
        .build();
 
    final long startNanos = System.nanoTime();
    final Call call = client.newCall(request);
 
    // Schedule a job to cancel the call in 1 second.
    executor.schedule(new Runnable() {
      @Override public void run() {
        System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f);
        call.cancel();
        System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f);
      }
    }, 1, TimeUnit.SECONDS);
 
    try {
      System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f);
      Response response = call.execute();
      System.out.printf("%.2f Call was expected to fail, but completed: %s%n",
          (System.nanoTime() - startNanos) / 1e9f, response);
    catch (IOException e) {
      System.out.printf("%.2f Call failed as expected: %s%n",
          (System.nanoTime() - startNanos) / 1e9f, e);
    }
}

超時

沒有響應時使用超時結束call。沒有響應的原因可能是客戶點鏈接問題、服務器可用性問題或者這之間的其他東西。OkHttp支持連接,讀取和寫入超時。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private final OkHttpClient client;
 
public ConfigureTimeouts() throws Exception {
    client = new OkHttpClient();
    client.setConnectTimeout(10, TimeUnit.SECONDS);
    client.setWriteTimeout(10, TimeUnit.SECONDS);
    client.setReadTimeout(30, TimeUnit.SECONDS);
}
 
public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
        .build();
 
    Response response = client.newCall(request).execute();
    System.out.println("Response completed: " + response);
}

每個call的配置

使用OkHttpClient,所有的HTTP Client配置包括代理設置、超時設置、緩存設置。當你需要爲單個call改變配置的時候,clone 一個OkHttpClient。這個api將會返回一個淺拷貝(shallow copy),你可以用來單獨自定義。下面的例子中,我們讓一個請求是500ms的超時、另一個是3000ms的超時。

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
private final OkHttpClient client = new OkHttpClient();
 
public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay.
        .build();
 
    try {
      Response response = client.clone() // Clone to make a customized OkHttp for this request.
          .setReadTimeout(500, TimeUnit.MILLISECONDS)
          .newCall(request)
          .execute();
      System.out.println("Response 1 succeeded: " + response);
    catch (IOException e) {
      System.out.println("Response 1 failed: " + e);
    }
 
    try {
      Response response = client.clone() // Clone to make a customized OkHttp for this request.
          .setReadTimeout(3000, TimeUnit.MILLISECONDS)
          .newCall(request)
          .execute();
      System.out.println("Response 2 succeeded: " + response);
    catch (IOException e) {
      System.out.println("Response 2 failed: " + e);
    }
}

處理驗證

這部分和HTTP AUTH有關。
相關資料:HTTP AUTH 那些事 - 王紹全的博客 - 博客頻道 - CSDN.NET

OkHttp會自動重試未驗證的請求。當響應是401 Not Authorized時,Authenticator會被要求提供證書。Authenticator的實現中需要建立一個新的包含證書的請求。如果沒有證書可用,返回null來跳過嘗試。

1
2
3
4
5
6
public List<Challenge> challenges()
Returns the authorization challenges appropriate for this response's code. 
If the response code is 401 unauthorized, 
this returns the "WWW-Authenticate" challenges.
If the response code is 407 proxy unauthorized, this returns the "Proxy-Authenticate" challenges.
Otherwise this returns an empty list of challenges.

當需要實現一個Basic challenge, 使用Credentials.basic(username, password)來編碼請求頭。

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
private final OkHttpClient client = new OkHttpClient();
 
public void run() throws Exception {
    client.setAuthenticator(new Authenticator() {
      @Override public Request authenticate(Proxy proxy, Response response) {
        System.out.println("Authenticating for response: " + response);
        System.out.println("Challenges: " + response.challenges());
        String credential = Credentials.basic("jesse""password1");
        return response.request().newBuilder()
            .header("Authorization", credential)
            .build();
      }
 
      @Override public Request authenticateProxy(Proxy proxy, Response response) {
        return null// Null indicates no attempt to authenticate.
      }
    });
 
    Request request = new Request.Builder()
        .build();
 
    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
 
    System.out.println(response.body().string());
}


轉自 OkHttp使用介紹 同時整合了 OkHttp使用進階 這篇文章以及其中的評論。

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0106/2275.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章