【Android】OkHttp简单入门

学而不思则罔,思而不学则殆


官方文档

https://github.com/square/okhttp

测试服务端

测试服务端https://github.com/aJanefish/ZyServer

导入JAR

根据需要选择导入的版本

    //Kotlin
    implementation "com.squareup.okhttp3:okhttp:4.9.0"
    //Java
    implementation "com.squareup.okhttp3:okhttp:3.14.9"

使用范例

同步请求+GET

    String run() throws IOException {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url("http://localhost:3434/okhttp")
                .build();

        try (Response response = client.newCall(request).execute()) {
            String body = response.body().string();
            ResponseShow.show(response, body);
            return body;
        }
    }

请求头:

GET http://localhost:3434/okhttp HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Host: localhost:3434
User-Agent: zy/test

响应头:

http/1.1 200 OK
Date: Wed, 23 Sep 2020 13:08:51 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)


{"code":200,"msg":"OK","data":""}

同步请求+POST+JSON

    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    OkHttpClient client = new OkHttpClient();

    String post() throws IOException {
        String json = "{\"name\":\"zy\",\"age\":18}";
        RequestBody body = RequestBody.create(JSON, json);
        Request request = new Request.Builder()
                .url("http://localhost:3434/okhttp")
                .post(body)
                .build();
        try (Response response = client.newCall(request).execute()) {
            String content = response.body().string();
            ResponseShow.show(response, content);
            return content;
        }
    }
POST http://localhost:3434/okhttp HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Content-Length: 22
Content-Type: application/json; charset=UTF-8
Host: localhost:3434
User-Agent: zy/test

{"name":"zy","age":18}
http/1.1 200 OK
Date: Wed, 23 Sep 2020 13:15:47 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)

{"code":200,"msg":"OK","data":"{\"name\":\"zy\",\"age\":18}"}

添加头部Headers

    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")
                .url("http://localhost:3434/okhttp")
                .header("User-Agent", "OkHttp Headers.java")
                .addHeader("Accept", "application/json; q=0.5")
                .addHeader("Accept", "application/vnd.github.v3+json")
                .build();

        try (Response response = client.newCall(request).execute()) {
            ResponseShow.show(response);
        }
    }
GET http://localhost:3434/okhttp HTTP/1.1
Accept: application/json; q=0.5
Accept-Encoding: gzip
Connection: keep-alive
Host: localhost:3434
User-Agent: OkHttp Headers.java

http/1.1 200 OK
Date: Wed, 23 Sep 2020 13:38:45 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)

{"code":200,"msg":"OK","data":""}

异步请求

    private final OkHttpClient client = new OkHttpClient();

    public void run() throws Exception {
        Request request = new Request.Builder()
                //.url("http://publicobject.com/helloworld.txt")
                .url("http://localhost:3434/okhttp")
                .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 {
                ResponseShow.show(response);
            }
        });
    }
GET http://localhost:3434/okhttp HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Host: localhost:3434
User-Agent: zy/test

http/1.1 200 OK
Date: Wed, 23 Sep 2020 13:46:00 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)

{"code":200,"msg":"OK","data":"GET from Server"}

Authenticate - 身份验证

public final class Authenticate {
    private final OkHttpClient client;
    public Authenticate() {
        // Give up, we've already attempted to authenticate.
        Authenticator authenticator = new Authenticator() {
            @Override
            public Request authenticate(Route route, Response response) throws IOException {
                if (response.request().header("Authorization") != null) {
                    return null; // Give up, we've already attempted to authenticate.
                }
                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();
            }
        };
        client = new OkHttpClient.Builder().authenticator(authenticator).build();
    }

    public void run() throws Exception {
        Request request = new Request.Builder()
                .url("http://publicobject.com/secrets/hellosecret.txt")
                .build();

        try (Response response = client.newCall(request).execute()) {
            ResponseShow.show(response);
        }
    }

    public static void main(String... args) throws Exception {
        new Authenticate().run();
    }
}
Authenticating for response: Response{protocol=http/1.1, code=401, message=Unauthorized, url=https://publicobject.com/secrets/hellosecret.txt}
Challenges: [Basic realm="OkHttp Secrets" charset="ISO-8859-1"]

----------------------------------------------
http/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Wed, 23 Sep 2020 13:48:40 GMT
Content-Type: text/plain
Content-Length: 1226
Last-Modified: Thu, 12 Jun 2014 01:53:57 GMT
Connection: keep-alive
ETag: "53990835-4ca"
Accept-Ranges: bytes


                      @@@@@\
                     @@@@@@@@
                    @@@@@@@@@@@@@@@@@@@.
                    @/ \@@@@@@@@@@@@@@@@.
                    @   @@@@@@@@@@@@@@@@+
                    @\ /@@@@@@@@"*@@/^@/
                    \@@@@@@@@@@/  "   "
                     @@@@@@@@
                      @@@@@/

     :@@@.
   .@@@@@@@:   +@@       `@@      @@`   @@     @@
  .@@@@'@@@@:  +@@       `@@      @@`   @@     @@
  @@@     @@@  +@@       `@@      @@`   @@     @@
 .@@       @@: +@@   @@@ `@@      @@` @@@@@@ @@@@@@  @@;@@@@@
 @@@       @@@ +@@  @@@  `@@      @@` @@@@@@ @@@@@@  @@@@@@@@@
 @@@       @@@ +@@ @@@   `@@@@@@@@@@`   @@     @@    @@@   :@@
 @@@       @@@ +@@@@@    `@@@@@@@@@@`   @@     @@    @@#    @@+
 @@@       @@@ +@@@@@+   `@@      @@`   @@     @@    @@:    @@#
  @@:     .@@` +@@@+@@   `@@      @@`   @@     @@    @@#    @@+
  @@@.   .@@@  +@@  @@@  `@@      @@`   @@     @@    @@@   ,@@
   @@@@@@@@@   +@@   @@@ `@@      @@`   @@@@   @@@@  @@@@#@@@@
    @@@@@@@    +@@   #@@ `@@      @@`   @@@@:  @@@@: @@'@@@@@
                                                     @@:
                                                     @@:
                                                     @@:

这种情况下会先返回401状态码,然后在携带Authorization的头部带上账号和密码。详情可以查看HTTP协议分享

缓存

public final class CacheResponse {
    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();

        String response1Body;
        try (Response response1 = client.newCall(request).execute()) {
            if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);

            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());
        }
        System.out.println("------------------------------------------------");
        String response2Body;
        try (Response response2 = client.newCall(request).execute()) {
            if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);

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

    }

    public static void main(String... args) throws Exception {
        new CacheResponse(new File("CacheResponse.tmp")).run();
    }
}
Response 1 response:          Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 1 cache response:    Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 1 network response:  null
------------------------------------------------
Response 2 response:          Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 2 cache response:    Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 2 network response:  null
Response 2 equals Response 1? true

缓存文件如下:
在这里插入图片描述
文件成对出现,xxx.0 保存响应头 xxx.1 保存正文
在这里插入图片描述
在这里插入图片描述

CancelCall 取消请求

    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();//1秒后取消请求
                System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f);
            }
        }, 1, TimeUnit.SECONDS);

        System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f);
        try (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);
            e.printStackTrace();
        }
    }

发送一个请求,服务端会延时两秒,客户端一秒后取消请求。

0.00 Executing call.
1.01 Canceling call.
1.01 Canceled call.
1.01 Call failed as expected: java.io.IOException: Canceled
java.io.IOException: Canceled
	at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:119)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
	at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
	at okhttp3.RealCall.execute(RealCall.java:77)
	at okhttp3.recipes.CancelCall.run(CancelCall.java:51)
	at okhttp3.recipes.CancelCall.main(CancelCall.java:62)

查看取消请求的源码:

  //RetryAndFollowUpInterceptor.java
  private volatile boolean canceled;
  public void cancel() {
    canceled = true;
    StreamAllocation streamAllocation = this.streamAllocation;
    if (streamAllocation != null) streamAllocation.cancel();
  }
  
  @Override public Response intercept(Chain chain) throws IOException {
    ...
    while (true) {
      if (canceled) {//取消请求,抛出IOException异常
        streamAllocation.release();
        throw new IOException("Canceled");
      }
      ...
    }
  }

追一下cancel的最终调用的是Socket.close方法:

  //RealConnection.java
  public void cancel() {
    // Close the raw socket so we don't end up doing synchronous I/O.
    closeQuietly(rawSocket);
  }
  
  public static void closeQuietly(Socket socket) {
    if (socket != null) {
      try {
        socket.close();
      } catch (AssertionError e) {
        if (!isAndroidGetsocknameError(e)) throw e;
      } catch (RuntimeException rethrown) {
        throw rethrown;
      } catch (Exception ignored) {
      }
    }
  }

证书

    private final OkHttpClient client;

    public CertificatePinning() {
        CertificatePinner certificatePinner = new CertificatePinner.Builder()
                .add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")//错误的
                //.add("publicobject.com", "sha256/C4f8sd7pdRY7B87OiUo20x3Dh9WVU0ZgJXX67BOKgWw=")//正确的
                .build();
        client = new OkHttpClient.Builder()
                .certificatePinner(certificatePinner)
                .build();
    }

    public void run() throws Exception {
        Request request = new Request.Builder()
                .url("https://publicobject.com/robots.txt")
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

            for (Certificate certificate : response.handshake().peerCertificates()) {
                System.out.println(CertificatePinner.pin(certificate));
            }
        }
    }

错误的证书:

Exception in thread "main" javax.net.ssl.SSLPeerUnverifiedException: Certificate pinning failure!
  Peer certificate chain:
    sha256/C4f8sd7pdRY7B87OiUo20x3Dh9WVU0ZgJXX67BOKgWw=: CN=privateobject.com
    sha256/YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=: CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US
    sha256/Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=: CN=DST Root CA X3, O=Digital Signature Trust Co.
  Pinned certificates for publicobject.com:
    sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=
	at okhttp3.CertificatePinner.check(CertificatePinner.java:204)
	at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:312)
	at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:268)
	at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:160)
	at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:256)
	at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:134)
	at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:113)
	at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
	at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
	at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
	at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:125)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
	at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
	at okhttp3.RealCall.execute(RealCall.java:77)
	at okhttp3.recipes.CertificatePinning.run(CertificatePinning.java:44)
	at okhttp3.recipes.CertificatePinning.main(CertificatePinning.java:54)

正确的证书:

http/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Wed, 23 Sep 2020 14:13:13 GMT
Content-Type: text/plain
Content-Length: 43
Last-Modified: Tue, 09 Jun 2009 00:00:00 GMT
Connection: keep-alive
ETag: "4a2da600-2b"
Accept-Ranges: bytes

User-agent: *
Disallow: /glazedlists/wiki/

----------------------------------------------

sha256/C4f8sd7pdRY7B87OiUo20x3Dh9WVU0ZgJXX67BOKgWw=
sha256/YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg

CheckHandshake

    /**
     * Rejects otherwise-trusted certificates.
     */
    private static final Interceptor CHECK_HANDSHAKE_INTERCEPTOR = new Interceptor() {
        Set<String> blacklist = Collections.singleton(
                "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=");

        @Override
        public Response intercept(Chain chain) throws IOException {
            for (Certificate certificate : chain.connection().handshake().peerCertificates()) {
                String pin = CertificatePinner.pin(certificate);
                System.out.println("pin:" + pin);
                if (blacklist.contains(pin)) {
                    throw new IOException("Blacklisted peer certificate: " + pin);//抛出异常
                }
            }
            return chain.proceed(chain.request());
        }
    };

    private final OkHttpClient client = new OkHttpClient.Builder()
            .addNetworkInterceptor(CHECK_HANDSHAKE_INTERCEPTOR)
            .build();

    public void run() throws Exception {
        Request request = new Request.Builder()
                .url("https://publicobject.com/helloworld.txt")
                .build();

        try (Response response = client.newCall(request).execute()) {
            ResponseShow.show(response);
        }
    }
pin:sha256/C4f8sd7pdRY7B87OiUo20x3Dh9WVU0ZgJXX67BOKgWw=
pin:sha256/YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=

----------------------------------------------
http/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Wed, 23 Sep 2020 14:15:18 GMT
Content-Type: text/plain
Content-Length: 1759
Last-Modified: Tue, 27 May 2014 02:35:47 GMT
Connection: keep-alive
ETag: "5383fa03-6df"
Accept-Ranges: bytes


                         \\           //
                          \\  .ooo.  //
                           .@@@@@@@@@.
                         :@@@@@@@@@@@@@:
                        :@@. '@@@@@' .@@:
                        @@@@@@@@@@@@@@@@@
                        @@@@@@@@@@@@@@@@@

                   :@@ :@@@@@@@@@@@@@@@@@. @@:
                   @@@ '@@@@@@@@@@@@@@@@@, @@@
                   @@@ '@@@@@@@@@@@@@@@@@, @@@
                   @@@ '@@@@@@@@@@@@@@@@@, @@@
                   @@@ '@@@@@@@@@@@@@@@@@, @@@
                   @@@ '@@@@@@@@@@@@@@@@@, @@@
                   @@@ '@@@@@@@@@@@@@@@@@, @@@
                        @@@@@@@@@@@@@@@@@
                        '@@@@@@@@@@@@@@@'
                           @@@@   @@@@
                           @@@@   @@@@
                           @@@@   @@@@
                           '@@'   '@@'

     :@@@.
   .@@@@@@@:   +@@       `@@      @@`   @@     @@
  .@@@@'@@@@:  +@@       `@@      @@`   @@     @@
  @@@     @@@  +@@       `@@      @@`   @@     @@
 .@@       @@: +@@   @@@ `@@      @@` @@@@@@ @@@@@@  @@;@@@@@
 @@@       @@@ +@@  @@@  `@@      @@` @@@@@@ @@@@@@  @@@@@@@@@
 @@@       @@@ +@@ @@@   `@@@@@@@@@@`   @@     @@    @@@   :@@
 @@@       @@@ +@@@@@    `@@@@@@@@@@`   @@     @@    @@#    @@+
 @@@       @@@ +@@@@@+   `@@      @@`   @@     @@    @@:    @@#
  @@:     .@@` +@@@+@@   `@@      @@`   @@     @@    @@#    @@+
  @@@.   .@@@  +@@  @@@  `@@      @@`   @@     @@    @@@   ,@@
   @@@@@@@@@   +@@   @@@ `@@      @@`   @@@@   @@@@  @@@@#@@@@
    @@@@@@@    +@@   #@@ `@@      @@`   @@@@:  @@@@: @@'@@@@@
                                                     @@:
                                                     @@:
                                                     @@:

----------------------------------------------

自定义超时时间

自定义超时时间,三个维度,连接超时,写入超时,读取超时。

    private final OkHttpClient client;

    public ConfigureTimeouts() throws Exception {
        client = new OkHttpClient.Builder()
                .connectTimeout(1, TimeUnit.SECONDS)
                .writeTimeout(1, TimeUnit.SECONDS)
                .readTimeout(1, 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();

        try (Response response = client.newCall(request).execute()) {
            System.out.println("Response completed: " + response);
        }
    }

超时过后抛出异常:

Exception in thread "main" java.net.SocketTimeoutException: Read timed out
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:171)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at okio.Okio$2.read(Okio.java:139)
	at okio.AsyncTimeout$2.read(AsyncTimeout.java:237)
	at okio.RealBufferedSource.indexOf(RealBufferedSource.java:345)
	at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:217)
	at okhttp3.internal.http1.Http1Codec.readHeaderLine(Http1Codec.java:212)
	at okhttp3.internal.http1.Http1Codec.readResponseHeaders(Http1Codec.java:189)
	at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:88)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
	at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
	at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
	at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
	at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:125)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
	at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
	at okhttp3.RealCall.execute(RealCall.java:77)
	at okhttp3.recipes.ConfigureTimeouts.run(ConfigureTimeouts.java:40)
	at okhttp3.recipes.ConfigureTimeouts.main(ConfigureTimeouts.java:46)

这三个维度的时间默认都是10s:

  //OkHttpClient.java
  public static final class Builder {
    ...
    public Builder() {
      ...
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }
}

设置时间:

  //RealConnection.java
  private void connectSocket(int connectTimeout, int readTimeout, Call call,
      EventListener eventListener) throws IOException {
    ...
    rawSocket.setSoTimeout(readTimeout);//设置读取时间
    try {
      Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
    } catch (ConnectException e) {
      ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
      ce.initCause(e);
      throw ce;
    }
    ...
  }

  //Platform.java
  public void connectSocket(Socket socket, InetSocketAddress address,
      int connectTimeout) throws IOException {
    socket.connect(address, connectTimeout);//设置连接时间
  }
  //RealConnection.java
  /**
   * To make an HTTPS connection over an HTTP proxy, send an unencrypted CONNECT request to create
   * the proxy connection. This may need to be retried if the proxy requires authorization.
   */
  private Request createTunnel(int readTimeout, int writeTimeout, Request tunnelRequest,
      HttpUrl url) throws IOException {
    // Make an SSL Tunnel on the first message pair of each SSL + proxy connection.
    String requestLine = "CONNECT " + Util.hostHeader(url, true) + " HTTP/1.1";
    while (true) {
      Http1Codec tunnelConnection = new Http1Codec(null, null, source, sink);
      source.timeout().timeout(readTimeout, MILLISECONDS); //设置读取时间
      sink.timeout().timeout(writeTimeout, MILLISECONDS);//设置写入时间
      ...
     }
  }

LoggingInterceptors - 自定义拦截

    private static final Logger logger = Logger.getLogger(LoggingInterceptors.class.getName());
    private final OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(new LoggingInterceptor())
            .build();

    public void run() throws Exception {
        Request request = new Request.Builder().url("https://publicobject.com/helloworld.txt").build();

        Response response = client.newCall(request).execute();
        response.body().close();
    }
    //自定义普通拦截器
    private static class LoggingInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            long t1 = System.nanoTime();
            Request request = chain.request();
            logger.info(String.format("Sending request %s on %s%n%s",
                    request.url(), chain.connection(), request.headers()));
            Response response = chain.proceed(request);

            long t2 = System.nanoTime();
            logger.info(String.format("Received response for %s in %.1fms%n%s",
                    request.url(), (t2 - t1) / 1e6d, response.headers()));
            return response;
        }
    }

效果:

九月 24, 2020 7:18:59 上午 okhttp3.recipes.LoggingInterceptors$LoggingInterceptor intercept
信息: Sending request https://publicobject.com/helloworld.txt on null

九月 24, 2020 7:19:01 上午 okhttp3.recipes.LoggingInterceptors$LoggingInterceptor intercept
信息: Received response for https://publicobject.com/helloworld.txt in 2651.0ms
Server: nginx/1.10.3 (Ubuntu)
Date: Wed, 23 Sep 2020 23:19:02 GMT
Content-Type: text/plain
Content-Length: 1759
Last-Modified: Tue, 27 May 2014 02:35:47 GMT
Connection: keep-alive
ETag: "5383fa03-6df"
Accept-Ranges: bytes

测试访问百度:

九月 24, 2020 7:19:56 上午 okhttp3.recipes.LoggingInterceptors$LoggingInterceptor intercept
信息: Sending request https://www.baidu.com/ on null

九月 24, 2020 7:19:56 上午 okhttp3.recipes.LoggingInterceptors$LoggingInterceptor intercept
信息: Received response for https://www.baidu.com/ in 211.6ms
Accept-Ranges: bytes
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 227
Content-Type: text/html
Date: Wed, 23 Sep 2020 23:19:57 GMT
P3p: CP=" OTI DSP COR IVA OUR IND COM "
P3p: CP=" OTI DSP COR IVA OUR IND COM "
Pragma: no-cache
Server: BWS/1.1
Set-Cookie: BD_NOT_HTTPS=1; path=/; Max-Age=300
Set-Cookie: BIDUPSID=11E0C713AFEC643D67097169E2DA6820; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: PSTM=1600903197; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BAIDUID=11E0C713AFEC643D84B10074D8C34F7E:FG=1; max-age=31536000; expires=Thu, 23-Sep-21 23:19:57 GMT; domain=.baidu.com; path=/; version=1; comment=bd
Strict-Transport-Security: max-age=0
Traceid: 1600903197025404673012799908543957356225
X-Ua-Compatible: IE=Edge,chrome=1

PerCallSettings

不同的请求不同的设置

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

        // Copy to customize OkHttp for this request.
        OkHttpClient client1 = client.newBuilder()
                .readTimeout(500, TimeUnit.MILLISECONDS)
                .build();
        try (Response response = client1.newCall(request).execute()) {
            System.out.println("Response 1 succeeded: " + response);
        } catch (IOException e) {
            System.out.println("Response 1 failed: " + e);
        }

        // Copy to customize OkHttp for this request.
        OkHttpClient client2 = client.newBuilder()
                .readTimeout(3000, TimeUnit.MILLISECONDS)
                .build();
        try (Response response = client2.newCall(request).execute()) {
            System.out.println("Response 2 succeeded: " + response);
        } catch (IOException e) {
            System.out.println("Response 2 failed: " + e);
        }
    }

结果是第一次请求(500<1000)会抛出异常,第二场正常(3000>1000)

Response 1 failed: java.net.SocketTimeoutException: timeout
Response 2 succeeded: Response{protocol=http/1.1, code=200, message=OK, url=http://httpbin.org/delay/1}

PostFile

    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("DiyREADME.md");

        Request request = new Request.Builder()
                //.url("https://api.github.com/markdown/raw")
                .url("http://localhost:3434/okhttp")
                .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
                .build();

        try (Response response = client.newCall(request).execute()) {
            ResponseShow.show(response);
        }
    }

文件内容如下:
在这里插入图片描述

请求头:

POST http://localhost:3434/okhttp HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Content-Length: 136
Content-Type: text/x-markdown; charset=utf-8
Host: localhost:3434
User-Agent: zy/test

OkHttp
======

An HTTP & HTTP/2 client for Android and Java applications. For more information see [the website][1] and [the wiki][2].

响应头:

http/1.1 200 OK
Date: Wed, 23 Sep 2020 23:36:45 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)

{"code":200,"msg":"OK","data":"POST from Server"}

PostForm

发送表单

    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")
                .url("http://localhost:3434/okhttp")
                .post(formBody)
                .build();

        try (Response response = client.newCall(request).execute()) {
            ResponseShow.show(response);
        }
    }
POST http://localhost:3434/okhttp HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Content-Length: 22
Content-Type: application/x-www-form-urlencoded
Host: localhost:3434
User-Agent: zy/test

search=Jurassic%20Park
http/1.1 200 OK
Date: Wed, 23 Sep 2020 23:40:59 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)

{"code":200,"msg":"OK","data":"POST from Server,Your Msg is :search=Jurassic%20Park"}

PostMultipart

post多个表单

    private static final String IMGUR_CLIENT_ID = "9199fdef135c122";
    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", "李二狗的生活")
                .addFormDataPart("name", "zy")
                .addFormDataPart("age", "18")
                .addFormDataPart("sex", "man")
                .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")
                .url("http://localhost:3434/okhttp")
                .post(requestBody)
                .build();

        try (Response response = client.newCall(request).execute()) {
            ResponseShow.show(response);
        }
    }

请求头:

POST http://localhost:3434/okhttp HTTP/1.1
Accept-Encoding: gzip
Authorization: Client-ID 9199fdef135c122
Connection: keep-alive
Content-Length: 4404
Content-Type: multipart/form-data; boundary=d51a0e43-79fd-46aa-b46a-49a83afec105
Host: localhost:3434
User-Agent: zy/test

--d51a0e43-79fd-46aa-b46a-49a83afec105
Content-Disposition: form-data; name="title"
Content-Length: 18

李二狗的生活
--d51a0e43-79fd-46aa-b46a-49a83afec105
Content-Disposition: form-data; name="name"
Content-Length: 2

zy
--d51a0e43-79fd-46aa-b46a-49a83afec105
Content-Disposition: form-data; name="age"
Content-Length: 2

18
--d51a0e43-79fd-46aa-b46a-49a83afec105
Content-Disposition: form-data; name="sex"
Content-Length: 3

man
--d51a0e43-79fd-46aa-b46a-49a83afec105
Content-Disposition: form-data; name="image"; filename="logo-square.png"
Content-Type: image/png
Content-Length: 3740

�PNG

IHDR   ?      ?݋   tEXtSoftware Adobe ImageReadyq�e<  $iTXtXML:com.adobe.xmp     <?xpacket begin="�?" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CS6 (Macintosh)" xmpMM:InstanceID="xmp.iid:FE3A4788A97A11E29AEC94190CEBECFC" xmpMM:DocumentID="xmp.did:FE3A4789A97A11E29AEC94190CEBECFC"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:FE3A4786A97A11E29AEC94190CEBECFC" stRef:documentID="xmp.did:FE3A4787A97A11E29AEC94190CEBECFC"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>?  IDATx��]�UU���Cm&�D'P���h��?*3mQ��??&-\?%��J�raA�PjVcE����C$?R�H�׈δ�u��???;}?=?5?�ֿf�s��g�}��???35���F�P?�
eB�B�P�P(*ʄ
�B�P�P&T(��Ai6����z���[:��?=,���CKO?P$ASSSe2��+,�i����y��-]m?K7?Stu?.]|���u(��	GX����`��ku*(��	{X������?*,��B��:�Ȅ�*h,ޥ�AQ
�m=8a?,��b?�OP�W��u��?,����]��YzX��?L?$Y�\���r���z�x�zK߳�����[���SS�c���5A�@?Fgfis??Yz�N=E?$a+⽞H��S����L��?�AcL$ L��h�����Lط��:,�v+��-}��?:=-}?:?K?,]�SPQ? �
�xdZ-ʹt?87��	:�E?.?�I�g��?:?
e����!?'��O�|?��?
��8�����ܷ\,��4A
/?>Kc�fMr?,�b��D����T�q��?.>I�n��g��?&�jC6@=��?-??A{w��
����+K�J�������,��bwW�f�g�a��:���Ǖ���X)��[�_
���Ӛ�`?)T�]ܚR��?0?4C}6�W�c�?2�tL��Ju
%H?�Ip~�I�_?&?<��Ź�T?Oup"U�nBrt�fN�e�T̅E2?|��MG7T}?"��L�}�ϢV$��?0?�qM�� mI��k��L�U�q��\���>��%?.>&%,?/y?;?s��/���RqU�\��VRZ��d~�k+U??? +�q��?	�a??g8?7������N?B?6�~Ԙ�R?>c�̊��h�H��~n"m*?$�ioO/o��?���W��/��*1?4?	V�gՎ?����i��+h�w�TS?i3as��:�Ȅ��M��qXbrσS(ʚ	d??W�c 5�b?
?Q]���B������I[L����N��F�~B�BQ>�B�P&T(?	
?2�B�L�P(?	
eB�B!?? :?iL\E?    IEND�B`?
--d51a0e43-79fd-46aa-b46a-49a83afec105--

最后一个是分段图片,有乱码。是分段传输,每一段都有自己的Content-Disposition,Content-Type,Content-Length等

响应头:

http/1.1 200 OK
Date: Wed, 23 Sep 2020 23:50:26 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)

{"code":200,"msg":"OK","data":"POST from Server,Your Msg is :--d51a0e43-79fd-46aa-b46a-49a83afec105\r\nContent-Disposition: form-data; name=\"title\"\r\nContent-Length: 18\r\n\r\n鏉庝簩鐙楃殑鐢熸椿\r\n--d51a0e43-79fd-46aa-b46a-49a83afec105\r\nContent-Disposition: form-data; name=\"name\"\r\nContent-Length: 2\r\n\r\nzy\r\n--d51a0e43-79fd-46aa-b46a-49a83afec105\r\nContent-Disposition: form-data; name=\"age\"\r\nContent-Length: 2\r\n\r\n18\r\n--d51a0e43-79fd-46aa-b46a-49a83afec105\r\nContent-Disposition: form-data; name=\"sex\"\r\nContent-Length: 3\r\n\r\nman\r\n--d51a0e43-79fd-46aa-b46a-49a83afec105\r\nContent-Disposition: form-data; name=\"image\"; filename=\"logo-square.png\"\r\nContent-Type: image/png\r\nContent-Length: 3740\r\n\r\n塒NG\r\n\u001a\n\u0000\u0000\u0000\rIHDR\u0000\u0000\u0000�\u0000\u0000\u0000<\b\u0006\u0000\u0000\u0000�\u0013輯\u0000\u0000\u0000\u0019tEXtSoftware\u0000Adobe ImageReadyq蒭<\u0000\u0000\u0003$iTXtXML:com.adobe.xmp\u0000\u0000\u0000\u0000\u0000<?xpacket begin=\"锘�\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?> <x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27        \"> <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"> <rdf:Description rdf:about=\"\" xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\" xmlns:xmpMM=\"http://ns.adobe.com/xap/1.0/mm/\" xmlns:stRef=\"http://ns.adobe.com/xap/1.0/sType/ResourceRef#\" xmp:CreatorTool=\"Adobe Photoshop CS6 (Macintosh)\" xmpMM:InstanceID=\"xmp.iid:FE3A4788A97A11E29AEC94190CEBECFC\" xmpMM:DocumentID=\"xmp.did:FE3A4789A97A11E29AEC94190CEBECFC\"> <xmpMM:DerivedFrom stRef:instanceID=\"xmp.iid:FE3A4786A97A11E29AEC94190CEBECFC\" stRef:documentID=\"xmp.did:FE3A4787A97A11E29AEC94190CEBECFC\"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end=\"r\"?>\u0013�\u0016\u0011\u0000\u0000\u000b\u000eIDATx陟]\u000b擴U\u0019廾菴\u0001m&〥'P\u0014抛鑘吋\u0016�*\u00103mQ墮�\u0019�&-\\\u0010�%欓J宺aA匬j\u0016Vc\u0005E\u0016\u0001\u0001C$�\u0014\u0013R襀堊埼纯u境�?�;鐪}�=�5�分縡焥髻g焳隹���35眄鞦�\u000e輙\b\u0014\neB匓橮(\u0013*\u0014蕜\n匓橮&T(\u0014徘Ai6抟意瀦伐蟍:且�\u0012=罂,鬋KO�\u0014P$ASSSe2+,輎│你y弗\u0013-]m�\u0006K7�\u0014St\u0005u�.K\u000b蕗\u0001]|菀飖\u001a(獫\tGX含屒`ku*(獣\t{X嚎\u0002漆圮�*\u0014U莿\u0013,剑B柒\u0003:\u001d\u0014杖劊*h,蕙覣Q\n\u001d=8a�\u0017,綉b?嶰P侇u触�,接\u0004啫]柖YzX�\u0014L�$Y馶摼卹ē鍠z弞K叱舯垍鎇栨甏S\u0014S\u001d峜嘛涒�\b\u001e5A怈�\u000bFg\u0018fis\u0004\u0003�\u0012蚧�\u0016Yz籒=E�$a\u001c\u001e+饨濰┹S敞殯L\u0010暢�\u0004慉cL\u0010$\u0000L丛h個\u0001參銵胤堶:,+售-}找\u000f�:=-}�\u0004\u0011:�\u0010K�,]PQ�\u0000顨\n�\u0017\r鈞d\u0016\u0006\u0004Z-痛t�87菀\t:\u0005\u0015欵�\u001f.\u0015骞�\u0017艻蚲篷�:�\ne曼鲀!�'兼O�\u000e♀�\n桡8瞟獍乙嬡穃,4A\n\u0015\u0016赖\u001e讔6丄Э\t�<m&H藗�\u0003诌蓣Y:乙洊朰�/~\u001bBj琮_Ix�~&�\u0003哯�扞f鐱?希蕩`�>|G郇\u0011麶耘飃饼�-瓀陉乡楚M鼏愿覮\u0010z\b7詶,綒�?u�\u0012\f帻\u000e贝擙賖閣蕜��\u001f[鶧潞�,5\u0015��\u0014�3,�%�5蠐掆<�'�\u001bSo灔)�\u000e樌\r\u0012JY\u0004\u000e<#闙臭i柉2仜(\t>h�\u001cL鄎2D\u0014\u0010忹9\u0013\u001d氨�\u0004馏慷4躌3�?`i糞鰾擇蔽岢瀗傡\u001c鹘贤聞譡歛仜壤婾\u001d屠'�\u0013珄�\"鱋菿<獉mC羮に�/g檇Ws5?乱jq就�(穤鬐F4�\u001aQo\f欮Z�\u0001!\u0001穜�\b1恈8叛b)�J壷拝\u0001!誺堛cM\u0010╭嬅��\u0004ls�\u0010\u0016�\u001f�\"\u001a\r藵\t_蟥麩\u0012麸^G]倸t�)L\b軆�&媍鳷/隣u\u0013{覜L\u0010\u001a\u0007\u001cGU鞔\b暪-侫�:ㄌ+8櫏D\u001c�\u0005獊惀ア雾柧c榄場�9\u0004^,\u0011荗ql\u001ax�\u0017y緸鉽幱熱烨Q;\u000b拽8�&�(朾躏\"q|\u0018'\u0013V鼰櫪g鴄Ko髄w綪� 9�4A栎=T崧�\u001bx廲,龜鐝p\fF怕\u0000氰\u0004塺姤谢�\u0014�\r折媱�\u001bl�6峤N\u0016迳\\t钺竾鐴倒炃薡\u000f齳囊\u001e@}\u001f舚\u000e%.躆?�=ae\u0000/j;W鍯y\u000eai2�\r/�>K\u000b\u0013\u0018c爁Mr�,隻D緞鳈T愔�\u0016.>I苙兂g趿�&鴍C6@=\u001f尿�-�?A{w差\n\u001e攲痪+K翵羶苓黎駠,棵bw\u0005W鄁猤漚海:枨晭报X\u0018)鲲[峗\n�&緲\u0017羳蠬�&$飌弙W�8\u0013\"觚�2a醼=)�$蒲\u00180�+交W滰祄b�6癘\u0019硫n狽绝B\t�\u00063\u001c尕阗6杂�<瘷\u0016褊'M�\u000e\t\u0017岔瀖遤\u0002讖�$<W櫚�\u0000昮\u0001�<0\u0016|勄\u0012嫺/捀@旓务搌溔+)<覽\u0011[\u001ai絔沜y詤+h_娘2\u0012i3%汜\u0011T莭{Hx�?�6嗛灠脖噷q?ツj撋k勥M~Q�\u001f抒灚:懘i犧d傘[=誫\t\u0018N緬皀\\傯 Q�>糋蘚os$�\u0001G\u0005蕭�\u0010徍=L鶀R�\u0005&枭B=B\u0014逝无1奈<顡缘鉩}n�\u0018鱬\u001e罖攌話象顖瀑\u001f耞�=傕擁%ǒPg乀?\u0002V顠p縂]\f跊\u0015 \u0019稾歮傪5`<US悒X=蟾G飫\u0013試\t;[�\u000fP捲�\u001dt7~A\u0000!\u000eNi甲�?�\u0006rt畈L�=襒撎簸駍>�\u000bdP溋2鰗O{\\+3)�\u000fq�(焛::02徂蝕�\u001f\u001a!晇s_�:餛鎾@=�\u0000\u000bB�\u0017咊何tt臈\u001d蕽\t7涾魻Ra毶8�1幾xJ�\u0010{E\u0019\u0001膕\u0004撓獭_版\u001d\u001b窕敹\r\u001e�\u000e帎凱城鸶)G&淧纖矪,D\u0017�;\u0013猽4wH)u剁礢Dy�(肔�(�\u0003Mn顔籦~梣�>\u0001颭掙\u001bK煈�<磤飂炐娻;=2G{D\u001fg罷&,3褥凁\u001f�$翇ΓI�迗v鏒▉賭靾茦:忕�\u0002⑼(圇r擓\u000fW\u0018媴炡�2SX�\u001a\u001c舴耻楉� \f丆胞Te买\u0005\"餺\u0010莝濄lc}�\tb=C繞�:筢\u001f�:\u0000\u0011%�\u0013鬵V律�樕�\u001f} �\t展K澟`~\u0002c\b$鄗O�8)f歀�\b\u0016\u0006\u0004H膃估捄樝RG[穆jg聖\u0015~瘺蓪!n\"覮Κ譆)偞\u001e�\u0010~�1\u001et剁僺�\u001f,#cb)%dc\u0016)俋S勍M\u0017啑�1D斍q倿樵镮fZk2)@Q諸<穼[滵�\u001a鶢�'\u001e\u0007p1�5'褴N觵@@.繼{勮骽\u0013M廷灺a徿G\u0004Y�\u0010罕\t撇�\r3uE糤Z&p\u0018B~a2駹X蒿埞�\u0011距}1礅c耡柶gI[)=眬卵逴\\碂�\"|�-�?a!$\u000bEb偮槻卪譺鎅e倶袣埮{\u0001ハOL�?剠\u0012壻璡t鰭�\u0007e賁�6櫶鰊1\u0002#厝叓檰マ�>f骙wr�9嘄\r眹嬘毊`�)T頬\u0012軞R化�0�4C}6鱓籧陙\u0019\u0010�2胻LJu\n%H?嘔p~矷鯻�\u0011&�<粭殴乀?Ou\u0018p\"U韓Brt唂N阤嶵\u001d虆E2�\u001b|倦MG7T}�\"牦㎜竲幭\u000e$優�0�\u0012鰍M睒 mI\u0018邕k嗒麹乁\u0011\u0017莙杏\\惍鈯>敁{\b%�.>\u001f&%,�/y�;�\u0006\u001a\u0018s起/槸峈q\u0005U芢屡VRZ崲d~\u000f+U�?� +\u0004猶\u001f�\t瀉\u001b�\u001b�\u0005g8\u0019�7\u0017煣仫嚺\u0002砃�B�6蓗詷躌�>c偺姵竓濰W~n\u0015\u001e\"m*�$猧oO/o顶�\u0005睉梂垺\u0005/杵*1\u001a�4\u0019�\tV鷊諑�\u001f尹燉i\u001b+h渨鑄S�\u0002i3as\u0005嵟\u0013:\u001d\u0014杖劙圡珋qXbr蟽S(蕷\t\u0001d�?W芻\u00005鬮�\n妀fB\u0000f铳e刃茙{o暯W檱Y\\�\u0010�Y\u001a焤@�\bL利J堹�\t\u001cF擓Fe5a紛2F�#f餺脨y\r�Q]墳\u0019\u000b\u0002麭嫬饷I[L愪N篁F獈B匓Q>戗B&T(�\t\u0015\n�2≒(�\t\u0015\neB匓!�?\u0001\u0006\u0000:�\u001diL\\E�\u0000\u0000\u0000\u0000IEND瓸`�\r\n--d51a0e43-79fd-46aa-b46a-49a83afec105--\r\n"}

PostString

发送字符串

    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")
                .url("http://localhost:3434/okhttp")
                .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
                .build();

        try (Response response = client.newCall(request).execute()) {
            ResponseShow.show(response);
        }
    }
POST http://localhost:3434/okhttp HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Content-Length: 88
Content-Type: text/x-markdown; charset=utf-8
Host: localhost:3434
User-Agent: zy/test

Releases
--------

 * _1.0_ May 6, 2013
 * _1.1_ June 15, 2013
 * _1.2_ August 11, 2013

http/1.1 200 OK
Date: Wed, 23 Sep 2020 23:56:09 GMT
Content-Type: application/json; charset=UTF-8
author: zy
Connection: close
Server: Jetty(9.3.2.v20150730)

{"code":200,"msg":"OK","data":"POST from Server,Your Msg is :Releases\n--------\n\n * _1.0_ May 6, 2013\n * _1.1_ June 15, 2013\n * _1.2_ August 11, 2013\n"}

PrintEvents

打印请求过程数据,可以用来做网络监控在项目中。

  private final OkHttpClient client = new OkHttpClient.Builder()
      .eventListenerFactory(PrintingEventListener.FACTORY)
      .build();

  public void run() throws Exception {
    Request washingtonPostRequest = new Request.Builder()
        .url("https://www.washingtonpost.com/")
        .build();
    client.newCall(washingtonPostRequest).enqueue(new Callback() {
      @Override public void onFailure(Call call, IOException e) {
      }

      @Override public void onResponse(Call call, Response response) throws IOException {
        try (ResponseBody body = response.body()) {
          // Consume and discard the response body.
          body.source().readByteString();
        }
      }
    });

    Request newYorkTimesRequest = new Request.Builder()
        .url("https://www.nytimes.com/")
        .build();
    client.newCall(newYorkTimesRequest).enqueue(new Callback() {
      @Override public void onFailure(Call call, IOException e) {
      }

      @Override public void onResponse(Call call, Response response) throws IOException {
        try (ResponseBody body = response.body()) {
          // Consume and discard the response body.
          body.source().readByteString();
        }
      }
    });
  }

  public static void main(String... args) throws Exception {
    new PrintEvents().run();
  }

  private static final class PrintingEventListener extends EventListener {
    private static final Factory FACTORY = new Factory() {
      final AtomicLong nextCallId = new AtomicLong(1L);

      @Override public EventListener create(Call call) {
        long callId = nextCallId.getAndIncrement();
        System.out.printf("%04d %s%n", callId, call.request().url());
        return new PrintingEventListener(callId, System.nanoTime());
      }
    };

    final long callId;
    final long callStartNanos;

    PrintingEventListener(long callId, long callStartNanos) {
      this.callId = callId;
      this.callStartNanos = callStartNanos;
    }

    private void printEvent(String name) {
      long elapsedNanos = System.nanoTime() - callStartNanos;
      System.out.printf("%04d %.3f %s%n", callId, elapsedNanos / 1000000000d, name);
    }

    @Override public void callStart(Call call) {
      printEvent("callStart");
    }

    @Override public void dnsStart(Call call, String domainName) {
      printEvent("dnsStart");
    }

    @Override public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {
      printEvent("dnsEnd");
    }

    @Override public void connectStart(
        Call call, InetSocketAddress inetSocketAddress, Proxy proxy) {
      printEvent("connectStart");
    }

    @Override public void secureConnectStart(Call call) {
      printEvent("secureConnectStart");
    }

    @Override public void secureConnectEnd(Call call, Handshake handshake) {
      printEvent("secureConnectEnd");
    }

    @Override public void connectEnd(
        Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol) {
      printEvent("connectEnd");
    }

    @Override public void connectFailed(Call call, InetSocketAddress inetSocketAddress, Proxy proxy,
        Protocol protocol, IOException ioe) {
      printEvent("connectFailed");
    }

    @Override public void connectionAcquired(Call call, Connection connection) {
      printEvent("connectionAcquired");
    }

    @Override public void connectionReleased(Call call, Connection connection) {
      printEvent("connectionReleased");
    }

    @Override public void requestHeadersStart(Call call) {
      printEvent("requestHeadersStart");
    }

    @Override public void requestHeadersEnd(Call call, Request request) {
      printEvent("requestHeadersEnd");
    }

    @Override public void requestBodyStart(Call call) {
      printEvent("requestBodyStart");
    }

    @Override public void requestBodyEnd(Call call, long byteCount) {
      printEvent("requestBodyEnd");
    }

    @Override public void responseHeadersStart(Call call) {
      printEvent("responseHeadersStart");
    }

    @Override public void responseHeadersEnd(Call call, Response response) {
      printEvent("responseHeadersEnd");
    }

    @Override public void responseBodyStart(Call call) {
      printEvent("responseBodyStart");
    }

    @Override public void responseBodyEnd(Call call, long byteCount) {
      printEvent("responseBodyEnd");
    }

    @Override public void callEnd(Call call) {
      printEvent("callEnd");
    }

    @Override public void callFailed(Call call, IOException ioe) {
      printEvent("callFailed");
    }
  }
0001 https://www.washingtonpost.com/
0001 0.001 callStart
0002 https://www.nytimes.com/
0002 0.000 callStart
0001 0.012 dnsStart
0002 0.009 dnsStart
0001 0.018 dnsEnd
0002 0.015 dnsEnd
0002 0.021 connectStart
0001 0.025 connectStart
0002 10.023 connectFailed
0001 10.027 connectFailed
0001 10.034 callFailed
0002 10.031 callFailed
0001 http://localhost:3434/okhttp
0001 0.000 callStart
0002 https://www.nytimes.com/
0002 0.000 callStart
0001 0.012 dnsStart
0002 0.008 dnsStart
0001 0.017 dnsEnd
0002 0.014 dnsEnd
0001 0.023 connectStart
0002 0.020 connectStart
0001 0.030 connectEnd
0001 0.030 connectionAcquired
0001 0.031 requestHeadersStart
0001 0.032 requestHeadersEnd
0001 0.034 responseHeadersStart
0001 0.037 responseHeadersEnd
0001 0.037 responseBodyStart
0001 0.041 responseBodyEnd
0001 0.042 connectionReleased
0001 0.042 callEnd

可以用来统计api中每个环节的耗时问题,提供数据,是否能够优化等。

服务端简单实现

服务端源码

public enum OkHttpTestHandler implements Route {
    GET {
        @Override
        public Object handle(Request request, Response response) throws Exception {
            System.out.println("GET...");
            String body = request.body();
            RequestShow.show(request, body);
            return OkHttpResp.create(200, "OK", "GET from Server,Your Msg is :" + body);
        }
    },

    POST {
        @Override
        public Object handle(Request request, Response response) throws Exception {
            try {
                System.out.println("post...");
                String body = request.body();
                System.out.println("body:" + body);
                RequestShow.show(request, body);
                return OkHttpResp.create(200, "OK", "POST from Server,Your Msg is :" + body);
            } catch (Exception e) {
                return OkHttpResp.create(404, e.toString());
            }
        }
    };
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值