Android okhttp3+retrofit2 Network Load efficiency optimization

Source: Internet
Author: User

First, development background:

What I'm doing now is a 3-year old project, when the project started with okhttp not as hot as it is now, basically using the HttpURLConnection class to implement all HTTP requests, then using the Xutils framework to implement asynchronous, callback-style interface requests. It is now found that there are a few big problems with the xutils framework.

Performance issues with the old framework:

1, xutils the image loading task will block the HTTP request, because xutils in the picture load frame bitmaputils and network request framework Httputils thread pool is shared, the size of this thread pool is default to 3, This means that when I download the image, it blocks the HTTP request Data Interface task. This will bring a serious consequence, when a page picture a lot of time, I open a new page, the new page needs to download the corresponding JSON string to display, but because the line constructor is full of pictures download task, so the user must wait until all the pictures are downloaded before you can tune the interface of the JSON, The page that would have been displayed very quickly now has to wait for the download of meaningless images, greatly reducing the user experience.

2, Xutils frame connection handshake is too frequent, according to the results of the packet capture, xutils after the completion of an HTTP request, will actively send a wave of fin message, the TCP connection is closed. In this case, if you frequently request the same server multiple times in the short term, then each time you have to re-three handshake steps, wasted a lot of time, according to the results of the packet capture, about each connection will waste about 300ms of time. Grab the bag as follows


It can be seen that the 3rd line is the Android client actively send fin messages to the server, and the time of transmission is immediately after the interface data transfer is complete. In other words, there is little connection keepalive, so if a short time to request the same interface multiple times, each call will perform a handshake, a large number of handshake will consume a lot of time, not suitable for the current app will be a lot of calls to the interface situation.

3, after the release of Android 6.0, Google has all the old version of Httpurlconnection,httpclient, and some of the Apache-related package classes and methods are defined as obsolete methods, and Android SDK 23 no longer built-in legacy classes and interfaces, Additional reference to the jar package is required, and for the robustness of the code it is also necessary to discard the old version of Android's HTTP framework.

Introduction and configuration of OkHttp3.0:

In my programming experience, the excellent open source framework will never be smooth sailing, okhttp is also the case, here is the introduction of the process of several big pits

First add a reference in Gradle (Eclipse can download a jar package directly into the project Okhttp-3.3.1.jar,okio-1.8.0.jar):

Compile ' com.squareup.okhttp3:okhttp:3.2.0 '
After importing a signed package, it's going to be a problem.

Note that these Gradle compiled error instructions note, do not affect the compilation process, and will not affect the APK package. These note is progard in the confusion of the time to find a duplicate of the class report, although it does not affect the use but here is to analyze, remove the duplicate class. The main point of the error is pointing to the old version of the old version of HttpURLConnection and HttpClient class, said they repeat, repeat the package is mainly ORG.APACHE.COMMONS.CODEC package.

So I searched the project for the reference to the package, first found that I am now the compilation version is 23, but 23 has no such classes, so I added the Apache old framework of the support jar package, in Build.gradle is configured like this:

Android {    compilesdkversion    buildtoolsversion "23.0.2"    defaultconfig {        ApplicationID " Com.xxx.app "        minsdkversion        targetsdkversion        versioncode 1        versionname ' 1.0.0.0 '        multidexenabled true    }    buildtypes {        release {            minifyenabled true            shrinkresources true            Proguardfiles getdefaultproguardfile (' proguard-android.txt '), ' Proguard-rules.pro '        }    }    productflavors {    }    sourcesets {        main {            jnilibs.srcdirs = [' Libs ']        }    }    packagingoptions {        exclude ' meta-inf/license.txt '    }    uselibrary ' Org.apache.http.legacy '    ...}
The point is that this sentence uselibrary ' org.apache.http.legacy '

He will automatically add the jar package in the SDK directory to the project, and there are some Apache classes in the jar package, \platforms\android-23\optional\org.apache.http.legacy.jar.

But how is it repeated? After some searching, I found a commons-codec.jar in the third-party library of Xutils.


It is this jar package and the class in the Org.apache.http.legacy.jar is duplicated, so the xutils third-party library Commons-codec.jar deleted, the note hint will be eliminated.

But that still doesn't compile, because the real problem is this:


The warning is also progard, and it is impossible to play the board without a solution. Online said this is related to NiO warning is okhttp in the process of compatibility with Android SDK 24 and Android M, the solution is very rude and brutal, find the project's Progard-rules.pro file, add such a line

okio.**
That is, do not report any Okio-related anomalies, you can continue to pack, is not very rude.

Second, Basic use of OKHTTP3:

Now that it's hard to import, just test it with a simple get and post request.

Join us to access such a Get interface: http://staging.qraved.com:8033/app/home/timeline?cityId=1&max=10

Then the native method for using Okhttp is as follows:

1. Assemble the GET Request URL:

The first thing to look at is the Okhttp framework: A Httpurl encapsulates the destination address and related parameters of a request, a request encapsulates all the relevant information for a requested, and finally the request object to The Okhttpclient object can perform the process of connecting to the server to obtain the data stream.

Mode 1: We can write the protocol, the host address, the interface address, the parameters separately, like the retrofit, as follows

Httpurl Httpurl = new Httpurl.builder (). Scheme ("http"). Host ("Staging.qraved.com"). Port (8033). Addpathsegments ("app/ Home/timeline "). Addqueryparameter (" Cityid "," 1 "). Addqueryparameter (" Max "," 2 "). Build (); Request Request = new Request.builder (). URL (httpurl). Get (). build ();

This is done first by Httpurl.builder () using the Factory mode to produce a Httpurl object, where the object is placed in a Request object.

Method 2: Use the string directly as a get access address

Request Request = new Request.builder (). URL ("http://staging.qraved.com:8033/app/home/timeline?cityId=1&max=10" ). Get (). Build ()

2, POST request URL and the assembly of parameters:

Here we use the Post method to request just the interface

Note here that the POST request parameters are encapsulated with Formbody.builder (), which differs from the OKHTTP2 class in that the package of a POST request is as follows

Request Request = new Request.builder (). URL ("Http://staging.qraved.com:8033/app/home/timeline"). Method ("POST", new Formbody.builder (). Add ("Cityid", "1"). Add ("Max", "2"). Build ()). build ();                
Note here the. Method () methods, GET request is not required to write this function, if the first parameter of the method is "GET", then do not follow the argument, otherwise it will be reported exception, because the GET request should not have the body

3. Execute the network request asynchronously:

Okhttp native gives the method of synchronous request and asynchronous request, so-called synchronization is the task that will block the current thread, generally need to put in the sub-thread, the asynchronous call using the interface callback, can be placed in the main line thread, the following first describes the method of asynchronous request (GET):

Okhttpclient client = new Okhttpclient ();                Request Request = new Request.builder (). URL (httpurl). Get (). build ();                    Client.newcall (Request) Enqueue (new OKHTTP3. Callback () {                        @Override public                        void OnFailure (OKHTTP3. Call call, IOException e) {                            log.i ("Alex", "Okhttp failed", e);                        }                        @Override public                        void Onresponse (OKHTTP3. Call Call, OKHTTP3. Response Response) throws IOException {                            log.i ("Alex", "Okhttp succeeded" +response.body (). String ());                        }                    );
In general, the above Respnse.body (). String () is a JSON string that requests a JSON interface to return.
Note Client.newcall (Request). The Enqueue () method is an async method, and the return value is a call object, which is equivalent to a request process, which has a cancel () method that cancels the current progress and yields the corresponding system resources. Freeing up memory and reducing CPU overhead is a convenient way to stop an HTTP request.

4. Synchronous execution of network requests

If you want to better control the download progress, it is recommended to use the synchronization method, but note to be placed in the sub-thread execution:

New Thread () {@Override public void run () {super.run (); Httpurl Httpurl = new Httpurl.builder (). Scheme ("http"). Host ("Staging.qraved.com"). Port (8033). Addpathsegments ("app/                Home/timeline "). Addqueryparameter (" Cityid "," 1 "). Addqueryparameter (" Max "," 2 "). Build ();                Okhttpclient client = new Okhttpclient ();                Request Request = new Request.builder (). URL (httpurl). Get (). build (); OKHTTP3.                Response Response = null;                    try {response = Client.newcall (Request). Execute ();//This will block thread} catch (IOException e) {                E.printstacktrace ();                    } if (response!=null) {String respbody = "";                        try {respbody = Response.body (). String ();                    LOG.I ("Alex", "request result is" +respbody);    } catch (IOException e) {e.printstacktrace ();                }}}}.start (); 
I like online constructor to do the synchronization method, for the thread pool scheduling later.

All right, okhttp, here's the basics, let's talk about how Retrofit2 works with OKHTTP3.

Third, the introduction of Retrofit2:

Since the reference to Okhttp has been added to the Retrofit2, we do not need to add the Okhttp reference in the Build.gradle, just one sentence

Compile ' com.squareup.retrofit2:retrofit:2.1.0 '
Can.

Retrofit2 has a lot of class names and OKHTTP3 exactly the same, their member methods are almost identical, this is the intentional design of retrofit, it is worth mentioning that Retrofit2 also by the call class, and the call usage of okhttp almost the same, All support the. Cancel () method, you can easily stop the download task in progress, very useful.

Send get and POST requests using retrofit

Retrofit2 is more like an annotation framework, he encapsulates the common operation of network access, makes the code look very simple and easy to understand, at first it will be a little bit uncomfortable (the starting time will be weird, maybe I personally don't like the reason for the annotated framework)

First, we need to encapsulate the URL, where we need to create a new interface, or the above URL as an example, this interface specifies whether the request is a GET or post, specify the key and number of request parameters, specify the address of the interface, as follows

Import OKHTTP3. Responsebody;import Retrofit2. Call;import retrofit2.http.get;import retrofit2.http.query;/** * Created by Administrator on 2016/6/27. */public interface TestInterface {    //interface Example     http://staging.qraved.com:8033/app/home/timeline?v=2.5.7& Client=1&cityid=2&userid=6092&max=10&minid=0    @GET ("/app/home/timeline?") Set is a GET request or a POST request    call<responsebody> Listrepos (@Query ("V") string V, @Query ("client") string client, @ Query ("userid") string userId, @Query ("MiniD") string MiniD, @Query ("Max") string max);
If you just want to get a string from the interface, then call's generic can be filled responsebody, if you want to parse the JSON into an object, then fill in the generic type of the class

Then you should fill in the relevant parameters, request the network, note that retrofit the host address and interface to separate the address, convenient for us to switch the server flexibly, as follows:

Retrofit Retrofit = new Retrofit.builder (). BASEURL ("http://staging.qraved.com:8033")//fill in the address of the host here        . build ();        TestInterface service = retrofit.create (Testinterface.class);        Call<responsebody> call = Service.listrepos ("2.5.7", "1", "6092", "0", "3");        LOG.I ("Alex", "body is" + call.request (). Body () + "URL is" + call.request (). URL () + "method is" + call.request (). method ()); Call.enqueue (New callback<responsebody> () {@Override public void Onresponse (call<re  Sponsebody> call, response<responsebody> Response) {try {log.i ("Alex", "success" +                Response.body (). String ());                } catch (IOException e) {e.printstacktrace ();                }} @Override public void OnFailure (call<responsebody> call, Throwable t) {            LOG.I ("Alex", "failure", t); }        });

Iv. on the optimization of thread scheduling:

In order to be compatible with some old models of old equipment, especially some memory small Android phones, we can not unlimited new threads, according to the above-mentioned Okhttp synchronization method, each request should be placed in a sub-line thread to operate, so now need a thread pool, and then let all network requests form a queue, The execution of a group of groups is then performed sequentially, which reduces the overhead of frequent thread creation and destruction, and reduces CPU and memory pressure.

But a thread pool is not enough, assuming that we have to frequently request a lot of interfaces, and in the middle of an interface time is particularly long, close to 60s, then this very time-consuming request will block a thread in the thread pool for a long time, resulting in other requests can not get the results on time, if this time-consuming request a few more, The user will feel that what to do is very card, even if some very small fast interface will be blocked by the large interface.

My solution in the project is to put the UI-related interface, which requires an immediate response, into an online constructor, a network request for background operations, a network request with little user relationships such as tracking events, getting updates, and some particularly time-consuming interfaces that are executed in a single thread, first guaranteeing a quick display of the UI interface. With such a fast and slow two thread pool, the user experience will be greatly improved.

V. On the optimization of connection keepalive:

The above mentioned Xutils will be sent immediately after the data request to send a fin message to turn off the current TCP connection, but Okhttp claims can be based on the pressure of the server automatic connection keepalive, through my packet capture results, Okhttp is actually not actively send fin messages, Wait for the server to actively request to close the connection, Okhttp will not send the keep-alive message, after the server's fin message arrives, Okhttp will close the current connection. If the client sends a new request before the connection times out, the connection is maintained so that a TCP port, with only one three-time handshake, continuously requests multiple interfaces or an interface request multiple times, saving three handshake times, which reduces the user's waiting time. The following capture package shows a three-time handshake, and then three calls to the same interface, and the last call after the end of 10s keepalive condition



Vi. on the timely cessation of meaningless network requests:

Sometimes some network requests may become a waste request, they not only crowding out the thread pool resources, accounting for memory, run more traffic, more power, and even cause memory leaks. For example, the user opened an activity, the activity opened a number of network requests, some request time is also very much, but the user saw no two eyes on the activity to turn off to see other, but these network requests are still a step-by-point implementation, which is meaningless. Because okhttp my favorite cancel function, these problems can be solved.

Let's talk about the processing logic of a framework called Okhttpfinal on GitHub:

It has a hashmap<string,list<call>>, and its key is a string for identifying activity and fragment, which is set by itself and can generally be used with activity.getclass () . GetName () as Key,value is an array, this array is all that activity or fragment establishes a network request. When I want to finish an activity or fragment, I pass the key into HashMap to get all the requests for the activity, and then cancel all of the one, so that the problem of the unused request is resolved.

By the way , a very bad place for this okhttpfinal, it uses a thread (that is, the system Asyntask) to perform all network requests, and if one request is particularly time-consuming, it will block other requests. I used this framework to change the source code to two thread pool to solve this problem.

Android okhttp3+retrofit2 Network Load efficiency optimization

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.