Preparation: HTTP details HTTP request packets and response packets

OkHttp3 is developed by Square, which is recognized as the best network request framework in Android. It is easy to use on interface encapsulation, GitHub address.

It has the following default features:

  • Supports HTTP/2, allowing all requests from the same host address to share the same socket connection
  • Use connection pooling to reduce request latency
  • Transparent GZIP compression reduces the size of response data
  • Cache the response content to avoid some completely duplicate requests

If your service has multiple IP addresses, when the first IP request fails, OkHttp will try alternate IP addresses that you have configured.

A, introducing

Gradle simply introduces dependencies.

    implementation 'com. Squareup. Okhttp3: okhttp: 3.14.7'
    implementation 'com. Squareup. Okio: okio: 1.17.5'
Copy the code

Version 3.14.x and earlier, written in Java language, after 4.0.0 using Kotlin language; The source code in this series is from version 3.14.x and explained in the Java language.

The Okio library complements java.io and java.nio to make it easier and faster to access, store, and process your data. The underlying OkHttp uses this library for support.

Also, don’t forget to apply for network request permissions. If you are using network request caching, then also apply for read and write permissions to external memory:

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Copy the code

Two, use mode

The basic steps are as follows

  • Build the client object OkHttpClient
  • Build Request Request
  • Generating a Call object
  • Call Initiates a request (synchronous/asynchronous)

The following is a detailed introduction with specific examples.

2.1 get request

Take baidu home page as an example to make a Get request:

        OkHttpClient httpClient = new OkHttpClient();

        String url = "https://www.baidu.com/";
        Request getRequest = new Request.Builder()
                .url(url)
                .get()
                .build();

        Call call = httpClient.newCall(getRequest);

        new Thread(new Runnable() {
            @Override
            public void run(a) {
                try {
                    // Synchronize the request to a child thread
                    Response response = call.execute();
                    Log.i(TAG, "okHttpGet run: response:"+ response.body().string());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
Copy the code

First, create the Request instance, then use request. Builder to build the Request instance and pass in the BAIDU home page URL, then httpClient. NewCall method passed in the Request instance to generate call, Finally, call call.execute() on the child thread to execute the request and get the response.

So, using OkHttp for GET requests is relatively simple, just changing the URL when building the Request instance.

One problem, you may have noticed, is that the request is executed on a child thread because call.execute() is a synchronous method. Do you want to use it directly in the main thread without manually creating child threads? Call.enqueue (callback) : call.enqueue(callback)

        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {}@Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.i(TAG, "okHttpGet enqueue: onResponse:"+ response.body().string()); }});Copy the code

Call. enqueue is executed asynchronously. Note that the two callback methods onFailure and onResponse are executed on the child thread, so if you want to perform UI operations, you need to switch to the UI thread using Handler.

Also, note that each Call can only be executed once (for reasons explained in the next process analysis).

The following output is displayed:

The 2020-05-04 21:52:56. 446, 32681-3631 / com. Hfy. Androidlearning I/OkHttpTestActivity: okHttpGet run: response: <! DOCTYPE html> <! --STATUS OK--><html> <head><meta http-equiv=content-type content=text/html; charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet Type = "text/CSS href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css > < title > baidu once, </title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su Value ="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com Name =tj_trnews class=mnav> news </a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a Href =http://map.baidu.com name=tj_trmap class=mnav> map </a> <a href=http://v.baidu.com name=tj_trvideo class=mnav> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav> </a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp; tpl=mn&amp; U = HTTP %3A%2F%2Fwww.baidu.com% 2F% 3fbdorz_come%3d1 Name =tj_login class=lb> login </a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window. The location. The search = = = ""??" ":" & ") + "bdorz_come = 1") + 'tj_login "name =" "class =" lb "> login < / a >'); </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;" </a> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy; 2017&nbsp; Baidu&nbsp; < a href=http://www.baidu.com/duty/ > before using baidu required < / a > & have spent <a href=http://jianyi.baidu.com/ class=cp-feedback> </a>&nbsp; Beijing ICP Certificate 030173 & NBSP; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>Copy the code

You can see baidu home page get request successfully responded.

2.2 a post request

2.2.1 POST Request to submit String and file

The difference between a POST Request and a GET Request is that when constructing the Request object, we need to construct an additional RequestBody object to carry the data we want to submit. The following is an example:

        OkHttpClient httpClient = new OkHttpClient();

        MediaType contentType = MediaType.parse("text/x-markdown; charset=utf-8");
        String content = "hello!";
        RequestBody body = RequestBody.create(contentType, content);

        Request getRequest = new Request.Builder()
                .url("https://api.github.com/markdown/raw")
                .post(body)
                .build();

        Call call = httpClient.newCall(getRequest);

        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {}@Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.i(TAG, "okHttpPost enqueue: \n onResponse:"+ response.toString() +"\n body:"+response.body().string()); }});Copy the code

Compare the GET Request to post(body) when building the Request and pass in the RequestBody instance. The RequestBody instance is created using the create method, specifying the RequestBody content type and RequestBody content. Here a text is passed in that is specified as markdown.

The result is printed as follows:

The 2020-05-05 13:18:26. 445, 13301-13542 / com. Hfy. Androidlearning I/OkHttpTestActivity: okHttpPost the enqueue: OnResponse: the Response {= HTTP / 1.1 protocol, code = 200, message = OK, url=https://api.github.com/markdown/raw} body: < p > hello! </p>Copy the code

The request succeeds and the body content is returned.

The MediaType passed in to the RequestBody can also be of other types, such as the client sending a JSON string to the background, or sending an image, which can be defined as:

// RequestBody:jsonBody, json string
String json = "jsonString";
RequestBody jsonBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);

//RequestBody:fileBody, upload file
File file = new File(Environment.getExternalStorageDirectory(), "1.png");
RequestBody fileBody = RequestBody.create(MediaType.parse("image/png"), file);
Copy the code

MediaType For more type information, see RFC 2045.

2.2.2 Post Request to submit the form

RequestBody is built in addition to the above approach and its subclass FormBody, which is used to submit form key-value pairs, meets most of the requirements of normal development.

//RequestBody:FormBody, a form key-value pair
RequestBody formBody = new FormBody.Builder()
        .add("username"."hfy")
        .add("password"."qaz")
        .build();
Copy the code

FormBody is created from formBody. Builder using the Builder mode, with add key-value pairs. Its contentType is already specified internally.

  private static final MediaType CONTENT_TYPE = MediaType.get("application/x-www-form-urlencoded");
Copy the code

2.2.2 POST Request Submit a complex request body

RequestBody Another subclass, MultipartBody, is used for POST requests to submit request bodies of complex types. Complex request bodies can contain multiple types of request body data at the same time.

The post request string, file, and form described above have a single type. Consider a registration scenario where the user fills in his/her name, phone number, and profile picture. The request body of the registration interface needs to accept form key-value pairs and files. So that’s MultipartBody. The complete code is as follows:

        OkHttpClient httpClient = new OkHttpClient();

// MediaType contentType = MediaType.parse("text/x-markdown; charset=utf-8");
// String content = "hello!" ;
// RequestBody body = RequestBody.create(contentType, content);

        //RequestBody:fileBody, upload file
        File file = drawableToFile(this, R.mipmap.bigpic, new File("00.jpg"));
        RequestBody fileBody = RequestBody.create(MediaType.parse("image/jpg"), file);


        / / RequestBody: multipartBody, multi-type (username, password, head)
        MultipartBody multipartBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("username"."hufeiyang")
                .addFormDataPart("phone"."123456")
                .addFormDataPart("touxiang"."00.png", fileBody)
                .build();


        Request getRequest = new Request.Builder()
                .url("http://yun918.cn/study/public/file_upload.php")
                .post(multipartBody)
                .build();

        Call call = httpClient.newCall(getRequest);

        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

                Log.i(TAG, "okHttpPost enqueue: \n onFailure:"+ call.request().toString() +"\n body:" +call.request().body().contentType()
                +"\n IOException:"+e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.i(TAG, "okHttpPost enqueue: \n onResponse:"+ response.toString() +"\n body:"+response.body().string()); }});Copy the code

As you can see, the MultipartBody instance was built using the MultiPartBody. Builder method, and the name and phone key-value pairs were passed in through the addFormDataPart method. AddFormDataPart (“touxiang”, “00.png”, fileBody), where “touxiang” is the key value, “00.png” is the file name, FileBody is a RequestBody created for uploaded images. Because all data is submitted as a FORM of key-value pairs, setType(multipartBody.form) is set.

Packet capture result:

Other Request methods, such as PUT, header, and DELETE, can be replaced by PUT (), header(), and delete() when constructing a Request. However, they are rarely used on Android.

2.4 Requesting configuration Items

Let’s start with a few questions:

  1. How do I set the timeout duration globally?
  2. Cache location, maximum cache size?
  3. Consider the requirement that I monitor all the original requests my App makes through OkHttp and how long the entire request takes.

These questions are very simple in OkHttp. Create an instance of OkHttpClient as follows:

        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(15, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10, TimeUnit.SECONDS)
                .cache(new Cache(getExternalCacheDir(),500 * 1024 * 1024))
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();
                        String url = request.url().toString();
                        Log.i(TAG, "intercept: proceed start: url"+ url+ ", at "+System.currentTimeMillis());
                        Response response = chain.proceed(request);
                        ResponseBody body = response.body();
                        Log.i(TAG, "intercept: proceed end: url"+ url+ ", at "+System.currentTimeMillis());
                        return response;
                    }
                })
                .build();
Copy the code

Okhttpclient.builder sets connection, read, and write timeouts in Builder mode, and uses cache() to pass in a cache instance of the cache directory and size.

Also notice that the addInterceptor() method is used to add the Interceptor instance and override the Intercept method. Interceptor means an Interceptor. The Intercept () method is called when the request begins to execute. Proceed (request) = chain.proceed(request) = ResponseBody (request); The third problem is solved by printing the log and time before and after chain.proceed(request) above.

How exactly Interceptor works will be covered in the next flow analysis.

In addition, OkHttpClient instances are typically globally unique, so that these basic configurations are unified and the connection pool maintained internally can be reused efficiently (as described in the next process analysis).

With global configuration, there can be some individual configuration for a single request.

        Request getRequest = new Request.Builder()
                .url("http://yun918.cn/study/public/file_upload.php")
                .post(multipartBody)
                .addHeader("key"."value")
                .cacheControl(CacheControl.FORCE_NETWORK)
                .build();
Copy the code

So this Request instance,

  • The request header is added using the addHeader() method.
  • Use cacheControl(Cachecontrol.force_network) to enable this request to use the network and not cache. (You can also set FORCE_CACHE only.)

Thanks to Okhttp3, the basic OkHttp usage is explained