OkHttp usage details

Today I learned OkHttp and made a summary here. I hope it can help people in need. Well, don't talk more nonsense and get to the point.

1, OkHttp introduction

OkHttp is an excellent network request framework. When it comes to network request framework, many people may think of volley. Volley is a network request framework provided by Google. My blog also has a blog dedicated to volley. The blog address is here** Android network request - Volley's use **Since Google provides a framework for network requests, why should we use OkHttp? It turns out that volley relies on HttpCient, and Google removes HttpCient from the SDK of Android 6.0, so OkHttp is becoming more and more popular

Today, we mainly introduce OkHttp's Get request, Post request, uploading and downloading files, uploading and downloading pictures and other functions.

_ Of course, before we start, we need to add OkHttp dependency library to the project. As for how to add OkHttp dependency to the project in Android studio, we won't repeat it here. In addition, OkHttp uses the builder mode. If you don't know about the builder mode, you can see this blog Builder mode of design mode_**

Add OkHttp dependency

In the corresponding Module of gradle Add in
compile 'com.squareup.okhttp3:okhttp:3.5.0'   
Then synchronize the project

2, OkHttp makes a Get request

It only takes four steps to complete a Get request using OkHttp.

1. Get OkHttpClient object

OkHttpClient client = new OkHttpClient();

2. Construct Request object

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

Here, we use the builder mode and chain call to specify the Get request and pass in the address of the Get request

If we need to pass the parameters during the get request, we can splice the parameters after the url in the following way

https:www.baidu.com?username=admin&password=admin

3. Encapsulate the Request as a Call

Call call = client.newCall(request);

4. Call synchronous or asynchronous request methods as needed

//When a synchronous call returns a Response, an IO exception will be thrown
Response response = call.execute();

//Call asynchronously and set the callback function
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        Toast.makeText(OkHttpActivity.this, "get failed", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onResponse(Call call, final Response response) throws IOException {
        final String res = response.body().string();
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                contentTv.setText(res);
            }
        });
    }
});

Step 4: there are some points to pay attention to

  1. Synchronous calls block the main thread, which is generally not applicable
  2. The callback function called asynchronously is in the sub thread. We can't update the UI in the sub thread. We need to handle it with the help of runOnUiThread() method or Handler

Do you think the above is over? Yes, the Get request step of OkHttp is just four steps, but when you try to open the application to load data, you find that the data is not loaded. This is a simple but common mistake. Add networking rights in AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />

3, OkHttp submits key value pairs for Post requests

Using OkHttp to make a Post request is very similar to making a Get request, which can be completed in only five steps.

1. Get OkHttpClient object

OkHttpClient client = new OkHttpClient();

2. Build FormBody and pass in parameters

FormBody formBody = new FormBody.Builder()
                .add("username", "admin")
                .add("password", "admin")
                .build();

3. Build the Request and pass the FormBody as a parameter of the Post method

final Request request = new Request.Builder()
                .url("http://www.jianshu.com/")
                .post(formBody)
                .build();

4. Encapsulate the Request as a Call

Call call = client.newCall(request);

5. Call the request and rewrite the callback method

call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        Toast.makeText(OkHttpActivity.this, "Post Failed", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        final String res = response.body().string();
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                contentTv.setText(res);
            }
        });
    }
});

After the above steps, a post request is completed. Of course, the above url parameters and parameters to be passed in should be passed in according to the actual situation. You will find that the steps of get and post request are very similar.

4, OkHttp Post request submission string

If you have mastered the above two basic steps, the following content is relatively simple

The above parameters of our post are added by constructing a FormBody through key value pairs. In fact, the post method needs to pass in a RequestBody object. FormBody is a subclass of RequestBody, but sometimes we often encounter the need to pass in a string, such as the client sending a json string to the server, In this case, another method needs to be used to construct a RequestBody, as shown below:

RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain;charset=utf-8"), "{username:admin;password:admin}");

In the above MediaType, we specify that the transmission is plain text, and the encoding method is utf-8. Through the above method, we can send json strings to the server.

Note: for the type of MidiaType, you can search Baidu for mime type to view relevant content, which will not be repeated here

5, OkHttp Post requests to upload files

After understanding the above one, the following one is even simpler. Here we pass a picture above as an example. Of course, you can also upload a txt file, which is OK

In fact, the most important thing is to build our own RequestBody, as shown in the figure below

File file = new File(Environment.getExternalStorageDirectory(), "1.png");
if (!file.exists()){
    Toast.makeText(this, "file does not exist", Toast.LENGTH_SHORT).show();
}else{
    RequestBody requestBody2 = RequestBody.create(MediaType.parse("application/octet-stream"), file);
}

Here we upload the 1.png picture under the root directory of the mobile phone SD card. The application / octet stream in the code means that our file is any binary data stream. Of course, you can also change it to a more specific image/png

Note: finally, remember the most important point: add the write permission of the memory card, and add the following code in the AndroidManifest.xml file:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

6, OkHttp Post request submission form

We often encounter user registration on the web page. You need to enter the user name, password and upload the avatar. This is actually a form. Next, let's see how to submit the form using OkHttp. After the above learning, you must also understand that the main difference is to construct different requestbodies and pass them to the post method

Because we use OkHttp3, we need to import another package okio.jar to continue the following content. We need to add the following code to the Gradle file of the module, and then synchronize the project

compile 'com.squareup.okio:okio:1.11.0'

Here we will use a MuiltipartBody, which is a subclass of RequestBody. We use this class to build a RequestBody when submitting a form. In the following code, we will send a form containing the user's name, password and avatar to the server

File file = new File(Environment.getExternalStorageDirectory(), "1.png");
if (!file.exists()){
    Toast.makeText(this, "file does not exist", Toast.LENGTH_SHORT).show();
    return;
}
RequestBody muiltipartBody = new MultipartBody.Builder()
        //Be sure to set this sentence
        .setType(MultipartBody.FORM)
        .addFormDataPart("username", "admin")//
        .addFormDataPart("password", "admin")//
        .addFormDataPart("myfile", "1.png", RequestBody.create(MediaType.parse("application/octet-stream"), file))
        .build();

The part of adding user name and password above is very similar to the method of submitting key value pairs we learned above. We should pay attention to the following points:

(1) If you submit a form, you must set the sentence setType(MultipartBody.FORM)

(2) The first parameter of the submitted file addFormDataPart(), myfile in the above code is a key similar to the key value pair, which is used by the server, similar to the name attribute in the web page form, for example:

<input type="file" name="myfile">

(3) The second parameter of the submitted file addFormDataPart() is the local name of the file. The third parameter is RequestBody, which contains the path of the file we want to upload and MidiaType

(4) Remember to add the memory card read-write permission in the AndroidManifest.xml file

7, OkHttp makes a get request to download files

In addition to the above functions, our most commonly used function is to download files from the network. Our following example will demonstrate downloading a file, storing it in the root directory of the storage card, downloading a picture from the network and displaying it in ImageView

1. Download a file from the network (here we take downloading a picture as an example)

public void downloadImg(View view){
    OkHttpClient client = new OkHttpClient();
    final Request request = new Request.Builder()
            .get()
            .url("https://www.baidu.com/img/bd_logo1.png")
            .build();
    Call call = client.newCall(request);
    call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            Log.e("moer", "onFailure: ");;
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            //Get byte stream
            InputStream is = response.body().byteStream();

            int len = 0;
            File file  = new File(Environment.getExternalStorageDirectory(), "n.png");
            FileOutputStream fos = new FileOutputStream(file);
            byte[] buf = new byte[128];

            while ((len = is.read(buf)) != -1){
                fos.write(buf, 0, len);
            }

            fos.flush();
            //Close flow
            fos.close();
            is.close();
        }
    });
}

You will find that the step is not different from the general Get request. The only difference is what we do in the callback function. We Get the byte stream of the picture and save it as a local picture

2. Download a picture from the network and set it in ImageView

In fact, after learning the above steps, you can download the picture locally and then set it in ImageView. Of course, the following is another method. Here, we use the decodeStream of BitmapFactory to directly convert the input stream of the picture into Bitmap, and then set it in ImageView. Only the code in onResponse() is given below

@Override
public void onResponse(Call call, Response response) throws IOException {
    InputStream is = response.body().byteStream();

    final Bitmap bitmap = BitmapFactory.decodeStream(is);
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            imageView.setImageBitmap(bitmap);
        }
    });

    is.close();
}

8, Add a progress bar to the upload and download of files

We have always said that the user experience is very important. When we download large files and the network speed is relatively slow, if we only download or upload in the background without showing the user a progress, it will be a very poor user experience. Next, we will simply show the progress. In fact, it is very simple

1. Display file download progress

This is just a demonstration. I just display the progress in a TextView. Of course, the progress is obtained in our callback function onResponse()

(1) Use response.body().contentLength() to get the total file size

(2) Increment the length of the buf we read each time in the while loop

@Override
public void onResponse(Call call, Response response) throws IOException {
    InputStream is = response.body().byteStream();
    long sum = 0L;
    //Total file size
    final long total = response.body().contentLength();
    int len = 0;
    File file  = new File(Environment.getExternalStorageDirectory(), "n.png");
    FileOutputStream fos = new FileOutputStream(file);
    byte[] buf = new byte[128];

    while ((len = is.read(buf)) != -1){
        fos.write(buf, 0, len);
        //Each increment
        sum += len;

        final long finalSum = sum;
        Log.d("pyh1", "onResponse: " + finalSum + "/" + total);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //Set progress to TextView
                contentTv.setText(finalSum + "/" + total);
            }
        });
    }
    fos.flush();
    fos.close();
    is.close();
}

2. Display file upload progress

It is troublesome to handle the upload progress, because the specific upload process is handled by OkHttp in the RequestBody, and OkHttp does not provide us with an interface for the upload progress. Here, our approach is to inherit the RequestBody from the user-defined class, then rewrite its methods, and expose the upload progress through the interface callback for our use.

public class CountingRequestBody extends RequestBody {
    //RequestBody that actually works
    private RequestBody delegate;
    //Callback listening
    private Listener listener;

    private CountingSink countingSink;

    /**
     * Constructor initializes member variables
     * @param delegate
     * @param listener
     */
    public CountingRequestBody(RequestBody delegate, Listener listener){
        this.delegate = delegate;
        this.listener = listener;
    }
    @Override
    public MediaType contentType() {
        return delegate.contentType();
    }

    @Override
    public void writeTo(BufferedSink sink) throws IOException {
        countingSink = new CountingSink(sink);
        //Convert CountingSink to BufferedSink for writeTo()
        BufferedSink bufferedSink = Okio.buffer(countingSink);
        delegate.writeTo(bufferedSink);
        bufferedSink.flush();
    }

    protected final class CountingSink extends ForwardingSink{
        private long byteWritten;
        public CountingSink(Sink delegate) {
            super(delegate);
        }

        /**
         * This method is called when uploading, calling the callback function to expose the upload progress. This method provides the buffer itself size.
         * @param source
         * @param byteCount
         * @throws IOException
         */
        @Override
        public void write(Buffer source, long byteCount) throws IOException {
            super.write(source, byteCount);
            byteWritten += byteCount;
            listener.onRequestProgress(byteWritten, contentLength());
        }
    }

    /**
     * Returns the total byte size of the file
     * If the file size acquisition fails, - 1 is returned
     * @return
     */
    @Override
    public long contentLength(){
        try {
            return delegate.contentLength();
        } catch (IOException e) {
            return -1;
        }
    }

    /**
     * Callback listening interface
     */
    public static interface Listener{
        /**
         * Expose upload progress
         * @param byteWritted  Size of bytes Uploaded
         * @param contentLength Total byte size of the file
         */
        void onRequestProgress(long byteWritted, long contentLength);
    }
}

The above code comments are very detailed and will not be explained here. Then we need to make the following changes when writing specific requests

File file = new File(Environment.getExternalStorageDirectory(), "1.png");
if (!file.exists()){
    Toast.makeText(this, "file does not exist", Toast.LENGTH_SHORT).show();
}else{
    RequestBody requestBody2 = RequestBody.create(MediaType.parse("application/octet-stream"), file);
}

//Use our own encapsulated classes
CountingRequestBody countingRequestBody = new CountingRequestBody(requestBody2, new CountingRequestBody.Listener() {
    @Override
    public void onRequestProgress(long byteWritted, long contentLength) {
        //Print progress
        Log.d("pyh", "speed of progress:" + byteWritted + "/" + contentLength);
    }
});

In fact, the above is to wrap a layer on the original RequestBody. Finally, in our use, we can pass in our CountingRequestBody object in the post() method.

9, Postscript

The above are some common summaries of OkHttp, hoping to help people in need

This article is transferred from https://juejin.cn/post/6844903527655931911 , in case of infringement, please contact to delete.

Posted on Fri, 03 Dec 2021 04:42:43 -0500 by ricmch