Android: understanding and using OkHttp

Posted by geek_girl_2020 on Sat, 29 Jan 2022 16:34:34 +0100

OkHttp

1. What is OkHttp

1. Network request development

Advantages and disadvantages of Http request Library in history

HttpURLConnection—>Apache HTTP Client—>Volley—->okHttp

2. Project open source address

https://github.com/square/okhttp

3. What is OkHttp

  • OKhttp is an open source project of network request. Android network request lightweight framework supports file upload and download and https.

2. The role of OkHttp

OkHttp is an efficient HTTP Library:

  • Support HTTP / 2. Http / 2 supports concurrency on a single TCP connection by using multiplexing technology, and sends or receives data by sending multiple requests at one time on a connection
  • If HTTP/2 is not available, connection pool reuse technology can also greatly reduce latency
  • Support GZIP, which can compress the download volume
  • Response caching can directly avoid repeated requests
  • It will automatically recover from many common connection problems
  • If your server is configured with multiple IP addresses, OkHttp will automatically try the next IP address when the first IP connection fails. OkHttp also handles the proxy server problem and SSL handshake failure

advantage

  • Using OkHttp does not require rewriting the network code in your program. OkHttp implements almost the same as Java net. Httpurlconnection is the same API. If you use Apache HttpClient, OkHttp also provides a corresponding OkHttp Apache module

3. Basic use of Okhttp

The basic use of Okhttp is explained from the following five aspects:

  • 1.Get request (synchronous and asynchronous)
  • 2.POST request form (key value)
  • 3.POST Request Submission (JSON/String / file, etc.)
  • 4. File download
  • 5. Request timeout setting

Add build gradle

compile 'com.squareup.okhttp3:okhttp:3.6.0'

3.1 composition of Http request and response

http request

Therefore, to complete an http request, a class library needs to include five parts: request method, request address, request protocol, request header and request body These are all in okhttp3 It is embodied in the class of request, which is the class representing http request See the following figure:


HttpUrl class represents the request address, String method represents the request method, Headers represents the request header, and RequestBody represents the request body Object tag is the flag used to cancel the http request. Let's ignore this first

http response

Response composition diagram:

It can be seen that it is generally composed of response header, response header and response body However, the first line of the response expresses too much information. HTTP/1.1 indicates the access protocol, 200 is the response code, and OK is the message describing the status

According to a single responsibility, we should not express so much content in the first line of a response In this case, our response should be composed of access protocol, response code, description information, response header and response body

3.2 composition of OkHttp request and response

Request okhp

Construct an http request and view the specific content of the request:

final Request request = new Request.Builder().url("https://github.com/").build();

Let's see what the request looks like in memory and whether it corresponds to the request method, request address, request header and request body one by one as we said above

OkHttp response

How the OkHttp library represents a response:

You can see that in the Response class, Protocol represents the Request Protocol, int code represents the Response code, String message represents the description information, Headers represents the Response header, and ResponseBody represents the Response body Of course, in addition, Request represents the Request held and Handshake represents the information during SSL/TLS Handshake Protocol verification. We won't ask these additional information for the time being

With the class composition of OkHttp response just mentioned, let's look at the contents of OkHttp response in memory after OkHttp request:

final Request request = new Request.Builder().url("https://github.com/").build();
Response response = client.newCall(request).execute();

3.3 GET request synchronization method

Synchronous get means waiting for an http request until a response is returned In between, the process will be blocked, so get cannot be executed in the main thread of Android, otherwise an error will be reported

For synchronization requests, you need to start the sub thread when requesting. After the request is successful, you need to jump to the UI thread to modify the UI.

public void getDatasync(){
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                OkHttpClient client = new OkHttpClient();//Create OkHttpClient object
                Request request = new Request.Builder()
                        .url("http://www.baidu.com ") / / request the interface. If you need to pass parameters, splice them behind the interface.
                        .build();//Create Request object
                Response response = null;
                response = client.newCall(request).execute();//Get the Response object
                if (response.isSuccessful()) {
                Log.d("kwwl","response.code()=="+response.code());
                Log.d("kwwl","response.message()=="+response.message());
                Log.d("kwwl","res=="+response.body().string());
                //At this time, the code is executed in the sub thread. To modify the UI, please use the handler to jump to the UI thread.
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }).start();
}

The printing result is as follows:

response.code()==200;
response.message()OK;
res{"code":200,"message":success};

OkHttpClient implements Call Factory interface is the factory class of Call, which is responsible for sending execution request and reading response
Request stands for Http request, through request Builder auxiliary class to build

client.newCall(request) returns a Call by passing in an http request Then execute the execute() method to synchronously obtain the Response representing the http request response.body() is the ResponseBody class, which represents the Response body

matters needing attention:

1,Response.code is the code in the http response line. If the access is successful, 200 is returned This is not set by the server, but comes with the http protocol. The code in res is set by the server. Pay attention to the difference between the two.

2,response.body().string() is essentially a read operation of the input stream, so it is also part of the network request, so this line of code must be placed in the sub thread.

3,response.body().string() can only be called once. It has a return value for the first time and will return null for the second time. The reason is: response body(). The essence of string () is the read operation of the input stream. Only when there is a write operation of the output stream of the server can the read operation of the client get the data. The write operation of the server is only executed once, so the read operation of the client can only be executed once, and null will be returned the second time.

4. The string() method of the response body is very convenient and efficient for small documents However, if the response body is too large (more than 1MB), avoid using the string() method because it will load the entire document into memory

5. For the response body exceeding 1MB, the response body should be processed in the way of stream This is consistent with our logic of dealing with xml documents. Small files can be loaded into memory for tree parsing, while large files must be stream parsed

Notes:

responseBody.string() gets the expression of the string, or ResponseBody Bytes() gets the representation of a byte array, both of which add the document to memory You can also use ResponseBody Charstream() and ResponseBody Bytestream() returns a stream for processing

3.4. GET request asynchronous method

Asynchronous GET refers to the http request executed in another working thread. The request will not block the current thread, so it can be used in the main thread of Android

This method does not need to start the sub thread again, but the callback method is executed in the sub thread, so you have to jump to the UI thread when updating the UI.

The following is to download a file in a worker thread and call back the Callback interface when the response is readable When the response header is ready, the Callback interface will be called, so it may be blocked when reading the response body OkHttp does not provide an asynchronous api to receive the response body at this stage.

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

The printing results and precautions of asynchronous request are the same as those of synchronous request. The biggest difference is that asynchronous requests do not need to start the sub thread. enqueue method will automatically put the network request part into the sub thread for execution.

matters needing attention:

  • 1. The onFailure method and onResponse of the callback interface are executed in the sub thread.
  • 2,response. body(). The string () method must also be placed in the child thread. After executing this line of code to get the result, jump to the UI thread to modify the UI.

3.5 post request method

Post requests are also divided into synchronous and asynchronous methods. The difference between synchronous and asynchronous is similar to the get method, so only the use method of post asynchronous requests is explained at this time.

private void postDataWithParame() {
    OkHttpClient client = new OkHttpClient();//Create the OkHttpClient object.
    FormBody.Builder formBody = new FormBody.Builder();//Create form request body
    formBody.add("username","zhangsan");//Pass key value pair parameters
    Request request = new Request.Builder()//Create a Request object.
            .url("http://www.baidu.com")
            .post(formBody.build())//Delivery request body
            .build();
    client.newCall(request).enqueue(new Callback() {. . . });//The use of callback method is the same as that of get asynchronous request, which is omitted at this time.
}


After reading the code, we will find that the request mode is not set to post in the post request, and recall that the request mode is not set to get in the get request, so how to distinguish the request mode? The focus is on request The post method of builder class, in request The builder object is initially created as a get request by default, so there is no need to set the request method in the get request. When the post method is called, the request method is modified to post. So this is a post request.

3.6. Summary of POST request transfer parameters

3.6.1. Submit String by Post

The following is to submit a request to the service using HTTP POST This example submits a markdown document to the web service and renders the markdown in HTML Because the whole request body is in memory, avoid using this api to submit large documents (greater than 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());
}

3.6.2 Post communication

POST the request body in the form of stream The content of the request body is generated by stream writing In this example, the stream is written directly to the BufferedSink of Okio Your program may use OutputStream. You can use BufferedSink Outputstream() OkHttp's underlying streaming and byte operations are based on Okio library, which is also another IO library developed by Square to fill the gap between I/O and NIO. The purpose is to provide a simple and easy-to-use interface to operate io

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

3.6.3 submission of documents by 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());
}

3.6.4. Submit forms by Post

Use formencoding builder to build the request body with the same effect as HTML tags Key value pairs will be encoded using an HTML compatible form of 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());
  }

3.7 other usage of POST

3.7.1. Extract response header

A typical HTTP avatar is a map < string, string >: each field has one or no value But some headers allow multiple values, like Guava's Multimap

For example:

The variable response header provided in the HTTP response is multivalued OkHttp's api tries to make these situations apply

  • When writing the request header, use the header(name, value) to set the unique name and value If there is already a value, the old one will be removed and a new one will be added Use addHeader(name, value) to add multiple values (add without removing existing ones)
  • When reading the response header, use header(name) to return the last name and value Usually, this is also the only name and value If there is no value, the header(name) will return null If you want to read all the values corresponding to the field, using headers(name) will return a list

In order to get all headers, the headers class supports access by 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"));
}

3.7.2. Use Gson to parse JSON response

Gson is a very convenient api library for converting between JSON and Java objects Here we use gson to parse the JSON response of Github API

Note: ResponseBody Charstream() parses the response body using the character set specified by the content type of the response header The default is 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;
  }

3.7.3 response cache

In order to cache responses, you need a cache directory that you can read and write, and a cache size limit This cache directory should be private, and untrusted programs should not be able to read the contents of the cache

It is wrong for a cache directory to have multiple cache accesses at the same time Most programs only need to call new OkHttp() once, configure the cache at the first call, and then just call this instance elsewhere Otherwise, the two cache examples interfere with each other, destroy the response cache, and may cause the program to crash

The response cache is configured using HTTP headers You can add cache control in the request header: Max stale = 3600, okhttp cache will support Your service determines how long the response is cached through the response header. For example, use 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 equals Response 1? " + response1Body.equals(response2Body));
}

If you need to use the cache for the resistance response, use CacheControl FORCE_ NETWORK. If the resistance response is required to use the network, use CacheControl FORCE_ CACHE.

warning
If you use FORCE_CACHE, but the response requires the use of the network. OkHttp will return a 504 Unsatisfiable Request response

3.8 comprehensive examples

Reference link
activity_main

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:orientation="horizontal">
 
        <Button
            android:id="@+id/syncGet"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Synchronization request"/>
 
        <Button
            android:id="@+id/asyncget"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Asynchronous request"/>
		
		<Button
            android:id="@+id/post"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Form submission"/>
 
        <Button
            android:id="@+id/fileDownload"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="File download"/>
 
    </LinearLayout>
 
    <TextView
        android:id="@+id/tv_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

MainActivity

package com.example.okhttpdemo;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button syncGet;
    private Button asyncget;
    private Button post;
    private Button fileDownload;
    private TextView tvtext;
    private String result;




    private static OkHttpClient client = new OkHttpClient();


    /**
     * The connection timeout is directly set here. In the static method, the initial call has been made before the constructor is called
     */
    static {
        client.newBuilder().connectTimeout(10, TimeUnit.SECONDS);
        client.newBuilder().readTimeout(10, TimeUnit.SECONDS);
        client.newBuilder().writeTimeout(10, TimeUnit.SECONDS);
    }

    private Request request;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initialize();
        initListener();

    }

    /**
     * event listeners 
     */
    private void initListener() {
        syncGet.setOnClickListener(this);
        asyncget.setOnClickListener(this);
        post.setOnClickListener(this);
        fileDownload.setOnClickListener(this);
    }


    /**
     * Initialize layout control
     */
    private void initialize() {

        syncGet = (Button) findViewById(R.id.syncGet);
        asyncget = (Button) findViewById(R.id.asyncget);
        post = (Button) findViewById(R.id.post);
        tvtext = (TextView) findViewById(R.id.tv_text);
        fileDownload = (Button) findViewById(R.id.fileDownload);
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.syncGet:
                initSyncData();
                break;
            case R.id.asyncget:
                initAsyncGet();
                break;
            case R.id.post:
                initPost();
                break;
            case R.id.fileDownload:
                downLoadFile();
                break;
            default: 
                break;
        }
    }



    /**
     * get Request synchronization method
     */
    private void initSyncData() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    request = new Request.Builder().url(Contants.SYNC_URL).build();
                    Response response = client.newCall(request).execute();
                    result = response.body().string();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            tvtext.setText(result);
                            Log.d("MainActivity", "hello");
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }

    /**
     * Asynchronous request
     */
    private void initAsyncGet() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                request = new Request.Builder().url(Contants.ASYNC_URL).build();
                client.newCall(request).enqueue(new Callback() {

                    /**
                     * @param call   It is an interface and a ready request that can be executed
                     *               It can be cancelled. For a request object, only a single request can be made
                     * @param e
                     */
                    @Override
                    public void onFailure(Call call, IOException e) {
                        Log.d("MainActivity", "request was aborted");
                    }

                    /**
                     *
                     * @param call
                     * @param response   Is a response request
                     * @throws IOException
                     */
                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        /**
                         * Get the response to the request, and then pass the body() String(), get the requested data
                         * It's better to use string() instead of toString ()
                         * toString()Each class has the ability to convert objects into strings
                         * string()Is to convert the flow into a string
                         */
                        result = response.body().string();
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                tvtext.setText(result);
                            }
                        });
                    }
                });
            }
        }).start();
    }

    /**
     * Form submission
     */
    private void initPost() {

        String url = "http://112.124.22.238:8081/course_api/banner/query";


        FormBody formBody = new FormBody.Builder()
                .add("type", "1")
                .build();
        request = new Request.Builder().url(url)
                .post(formBody).build();
        new Thread(new Runnable() {
            @Override
            public void run() {
                client.newCall(request).enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {

                    }

                    @Override
                    public void onResponse(Call call, final Response response) throws IOException {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                tvtext.setText("Submitted successfully");
                            }
                        });
                    }
                });
            }
        }).start();

    }

    /**
     * File download address
     */
    private void downLoadFile() {
        String url = "http://www.0551fangchan.com/images/keupload/20120917171535_49309.jpg";
        request = new Request.Builder().url(url).build();
        OkHttpClient client = new OkHttpClient();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

                //Turn the successful response of the request into a byte stream
                InputStream inputStream = response.body().byteStream();

                /**
                 * Add permissions here in the mainfests file
                 * <uses-permission android:name="android.permission.INTERNET"/>
                 * <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
                 */

                //The file output stream is used here
                FileOutputStream fileOutputStream = new FileOutputStream(new File("/sdcard/logo.jpg"));
               //Define a byte array
                byte[] buffer = new byte[2048];
                int len = 0;
                while ((len = inputStream.read(buffer)) != -1) {
                    //Write out to file
                    fileOutputStream.write(buffer, 0, len);
                }
                //Close output stream
                fileOutputStream.flush();
                Log.d("wuyinlei", "File download succeeded...");
            }
        });
    }


}

reference resources

1,https://blog.csdn.net/fightingXia/article/details/70947701
2,https://blog.csdn.net/chenzujie/article/details/46994073
3,https://blog.csdn.net/weixin_30700099/article/details/95962192
4,https://www.jianshu.com/p/5a12ae6d741a
5,https://www.jianshu.com/p/ca8a982a116b
6,https://wuyinlei.blog.csdn.net/article/details/50579564

Topics: Android OkHttp