OkHttp使用教程

Android系統提供了兩種HTTP通訊類,HttpURLConnection和HttpClient。php

儘管Google在大部分安卓版本中推薦使用HttpURLConnection,可是這個類相比HttpClient實在是太難用,太弱爆了。
OkHttp是一個相對成熟的解決方案,聽說Android4.4的源碼中能夠看到HttpURLConnection已經替換成OkHttp實現了。因此咱們更有理由相信OkHttp的強大。html

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

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

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

入門

官方資料

官方介紹
github源碼github

使用範圍

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

jar包準備

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

基本使用

HTTP GET

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中的響應。json

Response類:

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類

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

固然也能獲取到流的形式:api

public final InputStream byteStream()

HTTP POST

POST提交Json數據

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提供了很方便的方式來作這件事情。

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提供的異步方法,入門教程中並無提到,後面的高級教程裏會有解釋。

    • 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。

    • private final OkHttpClient client = new OkHttpClient();
       
      public void run() throws Exception {
          Request request = new Request.Builder()
              .url("http://publicobject.com/helloworld.txt")
              .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來接收響應體。

    • private final OkHttpClient client = new OkHttpClient();
       
      public void run() throws Exception {
          Request request = new Request.Builder()
              .url("http://publicobject.com/helloworld.txt")
              .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訪問。

  • private final OkHttpClient client = new OkHttpClient();
     
    public void run() throws Exception {
        Request request = new Request.Builder()
            .url("https://api.github.com/repos/square/okhttp/issues")
            .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)。

  • 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()來獲取。

  • 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方式提交文件

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

  • 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編碼來進行編碼。

  • 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可用的話,他們會被自動添加到請求頭中。

  • 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。

  • private final OkHttpClient client = new OkHttpClient();
    private final Gson gson = new Gson();
     
    public void run() throws Exception {
        Request request = new Request.Builder()
            .url("https://api.github.com/gists/c2a7c39532239ff261be")
            .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

  • 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()
            .url("http://publicobject.com/helloworld.txt")
            .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 equ

    擴展

    在這一節還提到了下面一句:
    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:

  • 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:

  • 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:

  • 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:

    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。

    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支持鏈接,讀取和寫入超時。

  • 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的超時。

  • 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來跳過嘗試。

  • 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)來編碼請求頭。

  • 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()
            .url("http://publicobject.com/secrets/hellosecret.txt")
            .build();
     
        Response response = client.newCall(request).execute();
        if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
     
        System.out.println(response.body().string());
    }
相關文章
相關標籤/搜索