okhttp使用简介

OkHttp专为android和java提供的网络请求库

  • 支持同步异步
  • 加载速度更快、节省带宽
  • 透明的gzip处理降低了下载数据的大小
  • 支持android2.3及以上,对于java,最低要求java1.7
  • HTTP / 2支持允许所有请求相同的主机共享一个套接字
  • API设计轻巧,基本上通过几行代码的链式调用即可获取结果
  • 如果HTTP / 2不可用,连接池可减少请求延迟(如果请求不支持HTTP/2协议,那么Okhttp会在内部维护一个连接池, 通过该连接池,可以对HTTP/1.x的连接进行重用,减少了延迟)
  • 支持自定义缓存时间、大小避免网络完全重复请求(请求的数据会进行相应的缓存处理,下次再进行请求时,如果服务器告知304(表明数据没有发生变化),那么就直接从缓存中读取数据,降低了重复请求的数量)
  • OkHttp 处理了很多网络疑难杂症:会从很多常用的连接问题中自动恢复,同时也处理了代理服务器问题和SSL握手失败问题。(如果您的服务器配置了多个IP地址,当第一个IP连接失败的时候,OkHttp会自动尝试下一个IP)

okhttp使用介绍

引入项目

这里直接贴出okhttp的开源地址

引入依赖:

1
compile 'com.squareup.okhttp3:okhttp:3.8.0'

核心类讲解

OkHttpClient

OkHttpClient表示HTTP请求的客户端类。在App中,我们应该只执行一次new OkHttpClient(),将其作为全局的实例进行保存,从而在App的各处都只使用这一个实例对象,这样所有的HTTP请求都可以共用Response缓存、共用线程池以及共用连接池。

  • OkHttpClient可以配置一些参数,比如超时时间、缓存目录、代理、Authenticator等,需要使用OkHttpClient.Builder。示例如下:
    1
    2
    3
    4
    5
    6
    OkHttpClient client = new OkHttpClient.Builder()
    .readTimeout(30, TimeUnit.SECONDS)
    .cache(cache)
    .proxy(proxy)
    .authenticator(authenticator)
    .build();
  • 特殊情况需要针对个别网站设置特殊参数,我们可以调用OkHttpClient对象的newBuilder()方法进行设置(如果之前有设置该属性,即本次设置覆盖上次)。示例如下:
    1
    2
    3
    4
    5
    OkHttpClient client = ...

    OkHttpClient clientWith60sTimeout = client.newBuilder()
    .connectTimeout(60, TimeUnit.SECONDS)
    .build();
Request

Request类封装了请求报文相关信息:Url地址、请求的方法(如GET、POST、PUT等)、各种请求头(如Content-Type、Cookie)以及其他可选请求体。一般通过内部类Request.Builder的链式调用来生成Request对象。

1
2
3
4
5
Request mRequest = new Request.Builder()
.header("Authorization","xxx")
.url(url)
.get()
.build();
Call

Call代表了一个实际的HTTP请求,它是连接Request和Response的桥梁,通过Request对象的newCall()方法可以得到一个Call对象。Call对象既支持同步获取数据,也可以异步获取数据。

  • Call对象execute()方法同步调用,会阻塞当前线程,返回一个esponse对象。
  • Call对象enqueue()方法异步调用,不会阻塞当前线程,需要一个Callback对象。成功回调Callback对象的onResponse方法,失败回调Callback对象的onFailure方法
Response

Response类封装了响应报文信息:状态吗(200、404等)、响应头(Content-Type、Server等)以及其他可选的响应体。通过Call对象的execute()方法获得Response对象,异步回调执行Callback对象的onResponse方法也可以获取Response对象。

ResponseBody

通过Response的body()方法可以得到响应体ResponseBody

  • 响应体必须最终要被关闭,否则会导致资源泄露、App运行变慢甚至崩溃。
  • ResponseBody和Response都实现了Closeable和AutoCloseable接口,它们都有close()方法,Response的close()方法内部直接调用了ResponseBody的close()方法,无论是同步调用execute()还是异步回调onResponse(),最终都需要关闭响应体,可以通过如下方法关闭响应体:
    • Response.close()
    • Response.body().close()
    • Response.body().source().close()
    • Response.body().charStream().close()
    • Response.body().byteString().close()
    • Response.body().bytes()
    • Response.body().string()
  • 对于同步调用,确保响应体被关闭的最简单的方式是使用try代码块,如下所示:
    1
    2
    3
    4
    Call call = client.newCall(request);
    try (Response response = call.execute()) {
    ... // Use the response.
    }
    将Response response = call.execute()放入到try()的括号之中,由于Response 实现了Closeable和AutoCloseable接口,这样对于编译器来说,会隐式地插入finally代码块,编译器会在该隐式的finally代码块中执行Response的close()方法。

响应体中的数据有可能很大,应该只读取一次响应体的数据。调用ResponseBody的bytes()或string()方法会将整个响应体数据写入到内存中,可以通过source()、byteStream()或charStream()进行流式处理。

官方示例

同步get

下载一个文件,打印它的头,并打印它的响应体作为一个字符串。

  • 响应体Response.body().string()方法对小文件,方便快捷。
  • 但如果响应体大(大于1 MIB),就需要避免使用Response.body().string()。因为它会加载整个文件到内存中。
  • Request 默认使用get方式请求
  • Response.isSuccessful()表示状态码在[200..300),这意味着请求已成功收到。
    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()
    .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

在一个工作线程上下载一个文件,当响应是可读的。在响应头已准备好后,回调。读取响应体仍可能阻塞。OkHttp目前不提供异步API部分接收响应体。

  • enqueue 的Callback回调所用的线程非UI线程
    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
    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(Call call, IOException e) {
    e.printStackTrace();
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    Headers responseHeaders = response.headers();
    for (int i = 0, size = responseHeaders.size(); i < size; i++) {
    System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
    }

    System.out.println(response.body().string());
    }
    });
    }
请求头和响应头

headers 类似与Map<String, String>每个字段有一个值或没有,但有些头允许多个值

  • header(name, value) 设置唯一的键值对,这会覆盖已有的值
  • addHeader(name, value) 来添加header时,不会移除已有的header
  • 读取header(name)
    • header(name)可用来获取最后的这个name的相应value。 通常这也是唯一的,如果没有就返回null
    • headers(name)可用来获取这个name相应的所有的value值。返回类型 List
      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()
      .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 a String

使用HTTP POST将请求主体发送到服务,这个示例是将markdown document上传到web服务将其转换成html文档,并发送给客户端。

  • 在该例中,请求体会放置在内存中,所以应该避免用该API发送超过1M的数据。
  • RequestBody就是请求体,一般可通过调用该类的5个重载的static的create()方法得到RequestBody对象。
  • create()方法第一个参数都是MediaType类型,它是指的是要传递的数据的MIME类型。MediaType对象包含了三种信息:type、subtype以及CharSet一般将这些信息传入parse()方法中,这样就能解析出MediaType对象,比如在上例中text/x-markdown; charset=utf-8,type值是text,表示是文本这一大类;/后面的x-markdown是subtype,表示是文本这一大类下的markdown这一小类;charset=utf-8则表示采用UTF-8编码。如果不知道某种类型数据的MIME类型,可以参见连接Media TypesMIME 参考手册,较详细地列出了所有的数据的MIME类型。
    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 Streaming

下面的示例演示了如何使用POST发送Stream流。示例代码使用到了Okio的buffered sink,你可能更喜欢使用OutputStream,你可以通过BufferedSink.outputStream()来获得。

  • 以下代码在实例化RequestBody对象的时候重写了contentType()和writeTo()方法
    • 覆写contentType()方法,返回markdown类型的MediaType
    • 覆写writeTo()方法,该方法会传入一个Okia的BufferedSink类型的对象,可以通过BufferedSink的各种write方法向其写入各种类型的数据,此例中用其writeUtf8方法向其中写入UTF-8的文本数据。也可以通过它的outputStream()方法,得到输出流OutputStream,从而通过OutputSteram向BufferedSink写入数据
      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());
      }
Posting a File

下面的代码演示了如何用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发送Form表单中的键值对

如果想用POST发送键值对字符串,可以使用在post()方法中传入FormBody对象,FormBody继承自RequestBody,类似于Web前端中的Form表单。可以通过FormBody.Builder构建FormBody。

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

注意:在发送数据之前,Android会对非ASCII码字符调用encodeURIComponent方法进行编码,例如”Jurassic Park”会编码成”Jurassic%20Park”,其中的空格符被编码成%20了,服务器端会其自动解码。

用POST发送multipart数据

我们可以通过Web前端的Form表单上传一个或多个文件,需要在post()方法中传入MultipartBody对象。MultipartBody继承自RequestBody,也表示请求体。只不过MultipartBody的内部是由多个part组成的,每个part就单独包含了一个RequestBody请求体,所以可以把MultipartBody看成是一个RequestBody的数组,而且可以分别给每个RequestBody单独设置请求头。

  • MultipartBody要通过其内部类MultipartBody.Builder进行构建。
  • 通过MultipartBody.Builder的setType()方法设置MultipartBody的MediaType类型,一般情况下,将该值设置为MultipartBody.FORM,即W3C定义的multipart/form-data类型,详见Forms in HTML documents
  • 通过MultipartBody.Builder的方法addFormDataPart(String name, String value)addFormDataPart(String name, String filename, RequestBody body)添加数据,其中前者添加的是字符串键值对数据,后者可以添加文件。
  • MultipartBody.Builder还提供了三个重载的addPart方法,其中通过addPart(Headers headers, RequestBody body)方法可以在添加RequestBody的时候,同时为其单独设置请求头。
    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
    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 MultipartBody.Builder()
    .setType(MultipartBody.FORM)
    .addFormDataPart("title", "Square Logo")
    .addFormDataPart("image", "logo-square.png",
    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是Google开源JSON处理的Java库

  • 主要是利用ResponseBody的charStream方法返回的Reader传给Gson的fromJson转换为java类
    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()
    .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;
    }
缓存响应结果

以下代码,我们对于同一个URL,我们先后发送了两个HTTP请求。第一次请求完成后,Okhttp将请求到的结果写入到了缓存目录中,进行了缓存。response1.networkResponse()返回了实际的数据,response1.cacheResponse()返回了null,这说明第一次HTTP请求的得到的响应是通过发送实际的网络请求,而不是来自于缓存。然后对同一个URL进行了第二次HTTP请求,response2.networkResponse()返回了null,response2.cacheResponse()返回了缓存数据,这说明第二次HTTP请求得到的响应来自于缓存,而不是网络请求。

  • 要缓存响应结果,您需要一个可以读取和写入的缓存目录,以及缓存大小的限制。缓存目录应该是私有的,不受信任的应用程序不应该能够读取其内容!
  • Okhttp中多个缓存实例同时访问同一个缓存目录会出错。大部分的应用程序应该调用一次new OkHttpClient(),然后为其配置缓存实例。然后在App的各个地方都使用这一个OkHttpClient实例对象,否则两个缓存实例会互相影响,导致App崩溃。
  • 响应缓存规则
    • 调用request.cacheControl(CacheControl.FORCE_NETWORK)方法,忽略缓存配置,强制发送网络请求。
    • 调用request.cacheControl(CacheControl.FORCE_CACHE)方法,忽略网络请求,强制读取缓存配置。如果缓存不存在,那么就返回504 Unsatisfiable Request错误。
    • 也可以向请求中中加入类似于Cache-Control: max-stale=3600之类的请求头,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
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.Builder()
.cache(cache)
.build();
}

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 equals Response 1? " + response1Body.equals(response2Body));
}
取消请求

以下示例,服务器端会有两秒的延时,在客户端发出请求1秒之后,请求还未完成,这时候通过cancel方法中止了Call,请求中断,并触发IOException异常。

  • 当activity等被销毁的时候,当前的okhttp请求也应该被终止。可以通过调用Call的cancel方法立即中止请求。
  • 如果线程正在写入Request或读取Response,那么会抛出IOException异常。同步请求和异步请求都可以被取消。
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);
}
}
设置超时
  • connectTimeout()设置 客户端与服务器建立连接的超时时间 (默认10秒)
  • writeTimeout()设置 客户端上传数据到服务器的超时时间 (默认10秒)
  • readTimeout()设置 客户端从服务器下载响应数据的超时时间 (默认10秒)
  • 如果HTTP请求的某一部分超时了,那么就会触发异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private final OkHttpClient client;

public ConfigureTimeouts() throws Exception {
client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
}

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);
}
处理身份验证

okhttp自动重试未经身份验证的请求。当一个响应401没有授权,鉴定人被要求提供凭据。实现应该生成一个新的请求,包括缺少的凭据。如果没有凭据可用,返回NULL跳过重试

  • 通过Response对象的challenges()方法可以得到第一次请求失败的授权相关的信息。
    • 如果响应码是401 unauthorized,那么会返回”WWW-Authenticate”相关信息,这种情况下,要执行OkHttpClient.Builder的authenticator()方法,在Authenticator对象的authenticate()中 对新的Request对象调用header(“Authorization”, credential)方法,设置其Authorization请求头
    • 如果Response的响应码是407 proxy unauthorized,那么会返回”Proxy-Authenticate”相关信息,表示不是最终的服务器要求客户端登录授权信息,而是客户端和服务器之间的代理服务器要求客户端登录授权信息,这时候要执行OkHttpClient.Builder的proxyAuthenticator()方法,在Authenticator对象的authenticate()中 对新的Request对象调用header(“Proxy-Authorization”, credential)方法,设置其Proxy-Authorization请求头。
    • 如果用户名密码有问题,那么Okhttp会一直用这个错误的登录信息尝试登录,我们应该判断如果之前已经用该用户名密码登录失败了,就不应该再次登录,这种情况下需要让Authenticator对象的authenticate()方法返回null,这就避免了没必要的重复尝试,代码片段如下所示:
      1
      2
      3
      if (credential.equals(response.request().header("Authorization"))) {
      return null; // 如果我们已经失败了这些凭据,不要重试。
      }
    • 你也可以限制重试次数,超过次数就跳过重试
      1
      2
      3
      if (responseCount(response) >= 3) {
      return null; // 如果我们失败3次,就放弃。
      }
      上面的代码依赖
      1
      2
      3
      4
      5
      6
      7
      private int responseCount(Response response) {
      int result = 1;
      while ((response = response.priorResponse()) != null) {
      result++;
      }
      return result;
      }
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;

public Authenticate() {
client = new OkHttpClient.Builder()
.authenticator(new Authenticator() {
@Override public Request authenticate(Route route, Response response) throws IOException {
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();
}
})
.build();
}

public void run() throws Exception {
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());
}

相关衍生

参考