This article has authorized “Yu Gang said” wechat public account exclusive original release

This article is out of date, see the latest documentation:RxHttp is an impressive Http request framework

Introduction to the

RxHttp is based on the secondary encapsulation of OkHttp, and seamless with RxJava, a chain can send a complete request. The main functions are as follows:

1. Support Gson, Xml, ProtoBuf, FastJson and other third-party data parsing tools

2. Any request mode such as Get, Post, Put, and Delete can be supported, and the request mode can be customized

3. Support the Activity/fragments/View/ViewModel/any class, the request will be automatically closed

4. Supports unified encryption and decryption, and can set the encryption and decryption for a single request

5. Supports adding public parameters/headers, and can set whether to add public parameters/headers for a single request

6. The most elegant implementation of file upload/download and progress monitoring, and support breakpoint download

7. The most elegant unified handling of errors in history without breaking Lambda expressions

8. Most elegant handling of multiple baseurLs and dynamic BaseurLs in history

9. It takes 30 seconds to learn and costs very little to learn

Gradle rely on

implementation 'com. Rxjava. RXHTTP: RXHTTP: 1.3.2'
// Annotate the handler and generate the RxHttp class to send the request in a chain
annotationProcessor 'com. Rxjava. RXHTTP: RXHTTP - compiler: 1.3.2'
// Manage RxJava and lifecycle, Activity/Fragment destruction, and automatically close unfinished requests
implementation 'com. Rxjava. Rxlife: rxlife: 1.1.0'
Copy the code

RxHttp source RxLife source

Initialize the

// Set the debug mode. In this mode, logs are printed
RxHttp.setDebug(boolean debug)
// This is not required. It can only be initialized once. The second time will throw an exception
RxHttp.init(OkHttpClient okHttpClient)
// Alternatively, there will be log output in debug mode
RxHttp.init(OkHttpClient okHttpClient, boolean debug)
Copy the code

This step is optional; not initializing or passing null means the default OkHttpClient object is used.

Question: The title does not say RxHttp, so do some initialization with HttpSender? Here's a catch, which will be solved later

Add public parameters/headers and reset the URL

RxHttp provides a static interface callback that is called back every time a request is made. RxHttp provides a static interface callback that is called back every time a request is made. And this callback is performed on the child thread (the thread callback is requested to execute)

RxHttp.setOnParamAssembly(new Function() {
    @Override
    public Param apply(Param p) { 
        Methos method = p.getMethod();
        if (method.isGet()) {// Add different parameters depending on the request
        } else if (method.isPost() {
        }
        return p.add("versionName"."1.0.0")// Add public parameters
                .addHeader("deviceType"."android"); // Add a common request header}});Copy the code

Then there are some requests that we don’t want to add public parameters/headers. How about RxHttp? Very simple, before initiating a request, set no public parameters, as follows:

Param param = Param.get("http://...")
    // Sets whether to decorate Param objects, that is, whether to add public arguments. The default is true
    .setAssemblyEnabled(false); // If set to false, the static interface above will not be called back
Copy the code

At this point, maybe you have questions, what is Param, the following for you to explain.

Param

First, let’s look at how to send a request

Param param = Param.get("http://...")
    .add("key"."value");
HttpSender.from(param)
    .subscribe(s -> { // Where s is a String, that is, the return result of the Http request
       // Successful callback
    }, throwable -> {
       // Fail callback
    });
Copy the code

Question: said a chain to send a request? Don’t worry, not to enlarge the recruitment of the time to this, I can tell you, Param is a request body is a role, we can determine the request through Param (such as: Get, Post, Put, Delete and other requests), add request parameters, add request header, add File object; The request is then sent through the HttpSender, passing in the Param object.

HttpSender

The HttpSender is used to initialize and set public parameters, and the HttpSender is used to send requests, so what role does it play? It’s a request sender, passing in a Param object with the from operator, and returning an RxJava Observable. In this case, we can use RxJava’s powerful operators to handle the logic (that’s the introduction, In this case, we just use the subscribe operator to subscribe to the observer.

RxHttp

Now, we officially enlarge recruit, the title said a good chain to send a request, since the brag, will go to achieve it. Take the example above and see how we can implement a chain on the code

  RxHttp.get("http://...")
        .add("key"."value")
        .asString()  
        .subscribe(s -> { // Where s is a String, that is, the return result of the Http request
           // Successful callback
        }, throwable -> {
           // Fail callback
        });
Copy the code

RxHttp: RxHttp: RxHttp: RxHttp: RxHttp: RxHttp: RxHttp: RxHttp: RxHttp: RxHttp: RxHttp: RxHttp RxHttp=Param+HttpSender, and has its own special mission. What to use will be explained later.

Now, why is our library called RxHttp, but we initialize, set public parameters, and so on with HttpSender? Because the RxHttp class is not in the RxHttp library, it is generated through the annotation handler. When we saw gradle dependencies earlier, we used

annotationProcessor 'com. Rxjava. RXHTTP: RXHTTP - compiler: 1.3.2'
Copy the code

The purpose of the annotation handler is to generate the RxHttp class in your project, so why not just write it to the library? As mentioned earlier, since it has its own mission, and that mission is that we can generate custom apis in RxHttp through annotations, let’s look at how annotations can be used.

Set baseUrl dynamically

In real world development, most developers will isolate baseUrl, and RxHttp also takes this into account. RxHttp configures baseUrl with @defaultDomain annotation and looks at the code

public class Url {
    @DefaultDomain(a)// Set this to the default domain name
    public static String baseUrl = "http://ip.taobao.com/";
}
Copy the code

Rebuild the project, and then we can send the request directly to the path, as follows:

  RxHttp.get("/service/getIpInfo.php")
        .add("key"."value")
        .asString()  
        .subscribe(s -> { // Where s is a String, that is, the return result of the Http request
           // Successful callback
        }, throwable -> {
           // Fail callback
        });
Copy the code

RxHttp will determine the URL before sending the request, and if there is no domain name, it will add the default domain name, which is baseUrl. Then, what if we don’t want to use the default domain name? RxHttp is also considered, providing an @domain annotation. Let’s look at the usage again:

public class Url {
    @Domain(name = "Update9158") // Set a non-default domain name. If name is not passed, the default value is the name of the variable
    public static String update = "http://update.9158.com";

    @DefaultDomain(a)// Set this to the default domain name
    public static String baseUrl = "http://ip.taobao.com/";
}
Copy the code

At this time to rebuild the project, will be in RxHttp class generated in a setDomainToUpdate9158IfAbsent () method, Update9158 character is the name of the specified name, then send the request can be like this:

  RxHttp.get("/service/getIpInfo.php")
        .setDomainToUpdate9158IfAbsent()
        .add("key"."value")
        .asString()  
        .subscribe(s -> { // Where s is a String, that is, the return result of the Http request
           // Successful callback
        }, throwable -> {
           // Fail callback
        });
Copy the code

At this point, RxHttp detects that the URL has been configured with a domain name and will not use the default domain name. Did likewise, setDomainToUpdate9158IfAbsent will detect the url configuration domain name, if the configuration, also won’t use we specify the domain name.

Note: the @domain annotation can be used in more than one place, while @defaultdomain () can only be used in one place, otherwise it will not compile. It makes sense that only one DefaultDomain is possible. This means that baseUrl can be changed dynamically. RxHttp always gets the latest baseUrl. How, isn’t that nice!! For more information on using annotations, see the RxHttp Annotations handler Generated API (4)

Next, we’ll look at how to send a Post request, how to automatically close requests for completion when an Activity/Fragment is destroyed, how to upload/download files and listen for progress, and how to automatically parse the results returned by Http into the objects we want.

Note: RxHttp is used for the following explanations

Post

  RxHttp.postForm("http://...")
        .add("key"."value")
        .asString()  
        .subscribe(s -> { // Where s is a String, that is, the return result of the Http request
           // Successful callback
        }, throwable -> {
           // Fail callback
        });
Copy the code

Get is rxhttp. Get, and Post is rxhttp. postForm. Beyond that, there is no difference

In reality, these default requests are obviously not sufficient for our needs. For example, what should I do if I want to send an encrypted POST request? This is where we need to customize the request. For a custom request mode, see RxHttp for the powerful Param class to send a request in a chain (3).

Activity destruction, automatically closing outstanding requests

In the above example, if the request is not completed while the Activity/Fragment is being destroyed, the Activity/Fragment cannot be reclaimed, resulting in a memory leak. This is a very serious problem, so how does RxHttp solve it? At this point, it’s time to introduce another library I wrote myself, RxLife, and see how to use it directly

  RxHttp.postForm("http://...")
        .add("key"."value")
        .asString()
        .as(RxLife.as(this)) // Add this phrase before subscribing to observers
        .subscribe(s -> {
           // Successful callback
        }, throwable -> {
           // Fail callback
        });
  / / or
  RxHttp.postForm("http://...")
        .add("key"."value")
        .asString()
        .as(RxLife.asOnMain(this)) //asOnMain can call back the observer on the main thread
        .subscribe(s -> {
           // Successful callback
        }, throwable -> {
           // Fail callback
        });
Copy the code

In this case, this is the LifecycleOwner object, which is an interface, and in this case we’re passing in the Activity, because the Activity implements the LifecycleOwner interface. When the Activity/Fragment is destroyed, the RxJava pipeline is interrupted, and when the pipeline is interrupted, the unfinished request is automatically closed. For those who do not know about RxLife, please check out Android RxLife, a lightweight RxJava lifecycle management library (1), which is not explained in detail here. We will use RxLife in the following explanations

File upload/download and monitor the progress

Using RxHttp, can be very elegant implementation of file upload/download and progress monitoring, how elegant? Direct code

File upload

  RxHttp.postForm("http://...") // Send a Post request in the Form Form
        .add("key"."value")
        .add("file1".new File("xxx/1.png")) // Add the file object
        .add("file2".new File("xxx/2.png"))
        .asString() // The asXXX operator is an asynchronous operation
        .as(RxLife.asOnMain(this))  // Perceive the life cycle and call back on the main thread
        .subscribe(s -> { 
            // Successful callback
        }, throwable -> {
            // Fail callback
        });
Copy the code

As you can see, a file upload is not much different from a normal POST request, except that on top of the post request, the add method is called to add the file object to be uploaded.

File download

  // File storage path
  String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk";
  RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
        .asDownload(destPath) // Note that the download operator is used and the local path is passed in
        .as(RxLife.asOnMain(this))  // Perceive the life cycle and call back on the main thread
        .subscribe(s -> {
            // If the file is successfully downloaded, call back to the file download path
        }, throwable -> {
            // Download failed
        });
Copy the code

The difference between a download and a normal request is that the download uses the asDownload operator, all else being the same.

Monitor file download progress

  // File storage path
  String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk";
  RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
        .asDownload(destPath, progress -> {
            // Download progress callback,0 to 100, only when the progress is updated, the maximum callback 101 times, the last callback file storage path
            int currentProgress = progress.getProgress(); // The current progress is 0-100
            long currentSize = progress.getCurrentSize(); // The size of the current downloaded bytes
            long totalSize = progress.getTotalSize();     // Total size of bytes to download
        }, AndroidSchedulers.mainThread())// Specify the main thread callback
        .as(RxLife.as(this)) // Perceive the life cycle
        .subscribe(s -> {// The value of s is String. Here is the file storage path
            // When the download is complete, process the related logic
        }, throwable -> {
            // Download failed, process related logic
        });
Copy the code

Monitor the file upload progress

  RxHttp.postForm("http://www.......") // Send a Post request in the Form Form
        .add("key1"."value1")// Add parameters, not required
        .add("file1".new File("xxx/1.png"))
        .asUpload(progress -> {
            // The upload progress callback ranges from 0 to 100. The callback is performed only when the progress is updated. The maximum number of callback times is 101 and the last callback is the Http execution result
            int currentProgress = progress.getProgress(); // The current progress is 0-100
            long currentSize = progress.getCurrentSize(); // The size of the current uploaded bytes
            long totalSize = progress.getTotalSize();     // Total size of bytes to upload
        }, AndroidSchedulers.mainThread()) // Specify the main thread callback
        .as(RxLife.as(this))  // Perceive the life cycle
        .subscribe(s -> { // S is a String type, determined by the generic type in SimpleParser
            // Upload successfully, process related logic
        }, throwable -> {
            // Upload failed, process related logic
        });
Copy the code

The upload progress monitor uses the asUpload(Consumer, Scheduler) method, and the rest of the operation is the same as the download progress monitor

The data Parser

In the above case, the observer gets the data type of String, and then in real life, we often need to parse the data into the object we want. RxHttp takes this into account. Now let’s look at how to get the object we want

We used the taobao access IP interface as test interface http://ip.taobao.com/service/getIpInfo.php?ip=63.223.108.42 corresponding data structure is as follows

public class Response {
    private int     code;
    private Address data;
    // Omit the set and get methods

    class Address {
        // Omitted some fields for simplicity
        private String country; / / country
        private String region; / / region
        private String city; / / the city
        // Omit the set and get methods}}Copy the code

Start sending a request

  RxHttp.get("http://ip.taobao.com/service/getIpInfo.php") / / Get request
        .add("ip"."63.223.108.42")// Add parameters
        .addHeader("accept"."* / *") // Add the request header
        .addHeader("connection"."Keep-Alive")
        .addHeader("user-agent"."Mozilla / 4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)")
        .asObject(Response.class)  // Return Observable
      
        object
      
        .as(RxLife.asOnMain(this))  // Perceive the life cycle and call back on the main thread
        .subscribe(response -> {
            // Successful callback
        }, throwable -> {
            // Fail callback
        });
Copy the code

As you can see, here we’re using the asObject operator, passing in response. class, and the Response variable that the observer gets is an object of type Response. How about that? That was easy. RxHttp provides us with a series of asXXX methods, let’s look at them:

asList
<T> Observable<T> asParser(Parser<T> parser)
RxHttp’s powerful data parsing function (2)

Finally, I have attached some common uses of RxHttp, as follows:

  RxHttp.postForm("/service/getIpInfo.php")       // Send a Post request in the Form Form
        .setDomainToUpdate9158IfAbsent()  // Manually set the Domain name. This method is generated using the @domain annotation
        .tag("RxHttp.get")          // Set a tag for a single request
        .setUrl("http://...")       // Reset the URL
        .setAssemblyEnabled(false)  // Set whether to add public arguments. The default is true
        .cacheControl(CacheControl.FORCE_NETWORK)  // Cache control
        .setParam(Param.postForm("http://..."))    // Set a new Param object
        .add(new HashMap<>())   // Add parameters via Map
        .add("int".1)          // Add an int parameter
        .add("float".1.28838 F) // Add a float parameter
        .add("double".1.28838) // Add type double
        .add("key1"."value1")  // Add a String parameter
        .add("key2"."value2".false) // Determine whether to add parameters based on the Boolean field at the end
        .add("file1".new File("xxx/1.png"))            // Add file objects
        .addHeader("headerKey1"."headerValue1")        // Add header information
        .addHeader("headerKey2"."headerValue2".false)// Add headers based on the Boolean field at the end
        .asString()  Return Observable
      
        object asXXX as an asynchronous operator
      
        // Aware of the lifecycle, and callback in the main thread, when the Activity/Fragment is destroyed, automatically close the unfinished request
        .as(RxLife.asOnMain(this))  
        .subscribe(s -> {    // Subscribe to the observer
            // Successful callback
        }, throwable -> {
            // Fail callback
        });
Copy the code

summary

This concludes the basic use of RxHttp. As you can see, using the RxHttp class, a chain can complete a complete Http request, which is simply a trilogy of requests:

  • First, determine the request mode and add the parameters
  • Then, identify the parser and specify the type to parse into
  • Finally, subscribe to the observer and start sending requests

All of the above cases involve these three steps. In the end, you will find that in addition to a series of powerful functions provided by RxHttp, no matter what request is written, it is extremely similar, as long as the RxHttp class, you can complete all the requests in a chain, greatly reducing the cost of learning.

Note: To generate the RxHttp class in your project, you need to use the annotation class at least once, otherwise the annotation will not be detected and will not be generated.

If you think RxHttp+RxLife is easy to use, please remember to give me star. If you have a good idea, please leave a message or contact me.

See other articles in the RxHttp series for more details

RxHttp: A link to send requests for powerful data parsing function (2)

RxHttp: A powerful Param class for sending requests in a chain (3)

RxHttp a chain of annotations Generated API (4)