Basic Concept Interpretation of OkHttp

Recently, in sorting out Android commonly used third-party framework related things, said that Android framework, nothing more than Android development in the common network, image caching, data interaction, optimization, page framework, among which the network as a basic part, I believe that you are more using OkHttp, and in the long connection there are Socket and webSocket, etc., OkHttp today to summarize for you. Relevant content, part of the reference network resources.

OkHttp introduction

OkHttp, as the most popular third-party library of Android, can be said to be used by most Android client programs. The underlying layer of Retrofit also uses OkHttp. Compared with Volley and other network request frameworks, OkHttp has the following characteristics:

  • HTTP/2 supports sharing a socket for all requests to access the same host. That is to say, support Google's SPDY protocol, if SPDY is not available, then reduce request latency through connection pool.
  • Connection pooling reduces request latency (if HTTP/2 is unavailable).
  • Transparent GZIP compression reduces download size.
  • Response caching completely avoids the network use of repeated requests.
  • When network problems occur, OkHttp automatically retries multiple IP addresses of a host
  • ...

OkHttp official address: http://square.github.io/okhttp/
OkHttp GitHub address: https://github.com/square/okhttp

Use example

OkHttp is also very simple to use, support Get, Post and other requests, and support the upload and download of files and other functions, it can be said that now you can deal with business situations, OkHttp can solve. Here are some simple examples.

Synchronize Get requests

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

However, it should be noted that the string() method on the response body is convenient and efficient for small documents, but if the response body is large (greater than 1MB), string() should be avoided because it loads the entire document into memory.

Asynchronous Get Request

Asynchronous use of enqueue for requests, such as:

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

Set Header

Typical HTTP headers work like a Map < String, String > with or without a value for each field. But some headers allow multiple values, such as Guava's Multiplane.

When setting request header information with Request, some information is not overwritten, such as addHeader(name, value), addHeader(name, value) is used to add a header without removing the existing header.

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

Upload string

Use HTTP POST to send request principals (such as files) to the server, because the entire request principal exists in memory at the same time, it should be avoided to use this API to upload large documents larger than 1MB. If it is a large file, you can use OKHttp's breakpoint continuation function.

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

Of course, OkHttp also supports request bodies such as uploading files in stream form.

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

File upload

File upload is relatively simple, just provide the path of File directly.

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

Upload table parameters

OkHtpp supports the use of FormBody.Builder to build a request body that works like an HTML < form > tag. Key-value pairs encode using a HTML-form-compatible URL encoding.

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

Multi-part requests

MultipartBody.Builder can construct complex request bodies that are compatible with HTML file upload forms. Each part of the multipart request body is itself a request body and can define its own head. If there are headers of their own, then these headers should describe part of the body, such as its Content-Disposition. Content-Length and Content-Type are added automatically when they are available.

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

Cache response settings

To set up the cache response, you need a cache directory for reading and writing, and a cache size limit. The cache directory should be private and untrusted applications cannot read its contents. It is wrong to have multiple caches access the same mixed directory at the same time. Most applications should call new OkHttpClient() only once, configure their caches, and use the same instance everywhere. Otherwise, the two cache instances will interfere with each other.

OkHttp also supports setting the time and size of the cache. For example, add Cache-Control:max-stale=3600 to set the size of the request header cache, and use Cache-Control:max-age=9600 to configure the response cache time.

Network timeout configuration

The network part may be due to connection problems, server availability problems or other reasons resulting in network request timeouts. So in use, the network timeout can be set according to the actual situation.

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

Cancellation request

OkHttp supports canceling network requests and uses Call.cancel() to immediately stop an ongoing call. If a thread is writing a request or reading a response, it will receive an IOException, and both synchronous and asynchronous calls can be cancelled.

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

Authentication request

If network requests involve authentication mechanism, OkHttp also provides Authenticator for application certificate authentication. The implementation of Authenticator should construct a new request containing missing certificates. If no certificates are available, return null to skip retries.

Use Response.challenges() to capture the patterns and domains of all authentication challenges. When a Basic challenge is completed, the request header is encoded using Credentials.basic(username, password). Examples are as follows:

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

All of OkHttp

OkHttp supports rewriting, redirection, follow-up and retry. OkHttp uses Call to model tasks that satisfy requests, but intermediate requests and responses are necessary. OkHttp provides two forms of Call:

  • Synchronous: Threads block until the response is readable;
  • Asynchronous: Queuing requests in one thread and getting callbacks in another thread when your response is readable.

The request can be cancelled from any thread. If the request has not been executed, the request will fail and IOException error will occur when the request fails.

OkHttp supports synchronous and asynchronous requests. For synchronous calls, you use your own thread and are responsible for managing how many requests you create at the same time. For asynchronous calls, Dispatcher implements a policy of maximum concurrent requests. You can set the maximum (default is 5) for each server and all the maximum (default is 64).

Keywords: OkHttp network github Android

Added by PHPcadet on Thu, 16 May 2019 18:27:16 +0300