1, the preface

It has been two weeks since RxHttp was published, and RxHttp has received a lot of good reviews. The Number of Github stars has officially exceeded 1000, which is very motivating for me. Thank you for your support.

Many people tell me that I hope RxHttp can support coroutine and cache these two big functions, this is not, very easy to use cache function, after reading this article, I believe you will fall in love with RxHttp again. In addition, the coroutine is already on the way, this will take a little more time, you wait patiently.

If you like RxHttp, welcome to the QQ group exchange: 378530627 (RxHttp&RxLife exchange group)

Gradle rely on

dependencies {
   // Three is required
   implementation 'com. LJX. RXHTTP: RXHTTP: 2.2.2'
   // From RXHTTP v2.2.2, you need to manually rely on okhttp
   implementation 'com. Squareup. Okhttp3: okhttp: 4.6.0'  
   kapt 'com. LJX. RXHTTP: RXHTTP - compiler: 2.2.2' // Generate the RxHttp class

   // The following are not required
   implementation 'com. LJX. Rxlife: rxlife - coroutine: 2.0.0'  // Manage the coroutine life cycle, page destruction, close requests
   implementation 'com. LJX. Rxlife2: rxlife - rxjava: 2.0.0'    // Manage the RxJava2 lifecycle, page destruction, close requests
   implementation 'com. LJX. Rxlife3: rxlife - rxjava: 3.0.0'    // Manage the RxJava3 lifecycle, page destruction, close requests

   RxHttp has GsonConverter built in by default
   implementation 'com. LJX. RXHTTP: converter - Jackson: 2.2.2'
   implementation 'com. LJX. RXHTTP: converter - fastjson: 2.2.2'
   implementation 'com. LJX. RXHTTP: converter - protobuf: 2.2.2'
   implementation 'com. LJX. RXHTTP: converter - simplexml: 2.2.2'
}
Copy the code

Note: For pure Java projects, use annotationProcessor instead of Kapt; The RxHttp class is generated after the dependency is over. Remember rebuild

2. Set the global cache policy

Rxhttpplugins.setcache (File, long, CacheMode, long) statically sets the global cache policy as follows:

public void initRxHttpCahce(Concent contet) {     
    // Set the cache directory to Android/data/{app package name directory}/cache/RxHttpCache
    File cacheDir = new File(context.getExternalCacheDir(), "RxHttpCache"); 
    // Set the maximum cache to 10M and cache validity to 60 seconds
    RxHttpPlugins.setCache(cacheDir, 10 * 1024 * 1024, CacheMode.REQUEST_NETWORK_FAILED_READ_CACHE, 60 * 1000); 
}
Copy the code

The above code, using the setCache method, sets the following four parameters:

CacheDir: cache storage directory: Android/data/{app package name directory}/cache/RxHttpCache

MaxSize: The maximum Size of the cache. The maximum Size is 10 MB. If this Size is exceeded, the least used cache will be automatically cleared according to the LRU algorithm

CacheMode: indicates the global CacheMode. CacheMode indicates that the network is requested first and the cache is read after the network fails

CacheVaildTime: Indicates the validity period of the global cache, which is 60 seconds

The third parameter is to set the global cache mode.

RxHttp provides a total of five internal caching modes, as follows:

  1. ONLY_NETWORK

    In this mode, only the network is requested and the cache is not processed. This is the default RxHttp caching mode

  2. ONLY_CACHE

    In this mode, only the cache is read. If the data is read successfully, the data is returned directly. Otherwise, throw an exception

  3. NETWORK_SUCCESS_WRITE_CACHE

    In this mode, the network is directly requested. If the request succeeds, it is written into the cache and returned. Otherwise it just returns

  4. READ_CACHE_FAILED_REQUEST_NETWORK

    In this mode, the cache is read first, and if the read succeeds, it is returned directly. Otherwise the network will be requested (request succeeded, write to cache)

  5. REQUEST_NETWORK_FAILED_READ_CACHE

    In this mode, the network is first requested, the request succeeds, and the request is written to the cache and returned. Otherwise read the cache

Set the caching policy for a single request

Rxhttp#setCache(CacheMode) sets a separate cache policy for each request as follows:

RxHttp.get("/service/...")
    .setCacheValidTime(10 * 1000) // The current request cache is valid for 10 seconds
    .setCache(CacheMode.READ_CACHE_FAILED_REQUEST_NETWORK) // The current request reads the cache first, fails, then requests the network
    .asString()
    .subscribe(s -> {
        // Successful callback
    }, throwable -> {
        // Fail callback
    });              
Copy the code

The above code sets a separate cache policy for each request, which is valid for 10 seconds, and the cache mode is to read the cache first, and then request the network after the failure. If not set, the global cache policy will be used.

Note: Before setting a separate cache mode/cache duration, be sure to set the cache storage directory and cache maximum Size, otherwise it will be invalid

4. About CacheKey

CacheKey plays an important role in the cache module of RxHttp. Internal cache reads, writes, and deletes are performed through CacheKey. Make sure the CacheKey is unique for different requests; These two features are important to keep the CacheKey static for the same request, or the cache will become meaningless.

Why do you say that? Read on.

4.1 Rules for generating internal CacheKeys

RxHttp internally generates a unique CacheKey for each request according to the following rules:

NoBodyParam:

Concatenate the added parameters after the URL, for example: CacheKey = url? key=value&key1=value1… , Get, Head requests follow this rule

FormParam:

With NoBodyParam, concatenate the added parameters to the URL, for example: CacheKey = url? key=value&key1=value1… , xxxForm type requests follow this rule

JsonParam:

After converting the added parameter to the Json string jsonStr, add it to the url, for example: CacheKey = URL? Json =jsonStr, xxxJson type requests follow this rule

JsonArrayParam:

As with JsonParam, add the parameter to the URL after converting it to a Json string jsonStr, for example: CacheKey = URL? Json =jsonStr, xxxJsonArray requests follow this rule

The above four rules can be viewed in the getCacheKey() method of the corresponding Param class

Customize CacheKey

If the above rules do not satisfy your business needs, RxHttp also provides two ways to customize a CacheKey, as follows:

1. Specify a CacheKey for a single request using the RxHttp#setCacheKey(String) method

RxHttp.get("/service/...")
    .setCacheKey("Custom CacheKey")  // Customize CacheKey
    .asString()
    .subscribe(s -> {
       // Successful callback
    }, throwable -> {
       // Fail callback
    });
Copy the code

Define a CacheKey generation rule for a particular type of request by customizing Param and overriding getCacheKey(), as follows:

@Param(methodName = "postEncryptForm")
public class PostEncryptFormParam extends FormParam {

    public PostEncryptFormParam(String url) {
        super(url, Method.POST);
    }

    @Override
    public String getCacheKey(a) {
        String cacheKey = null;
        String simpleUrl = getSimpleUrl(); / / get the Url
        Headers headers = getHeaders();// Get the added header
        List<KeyValuePair> keyValuePairs = getKeyValuePairs(); // Get the added parameter
        cacheKey = "Generate CacheKey based on URL/request header/parameter, custom rule";
        returncacheKey; }}Copy the code

Note: When customizing a CacheKey, you must ensure that the CacheKey is unique. Repeating the CacheKey will overwrite an existing cache

4.3 uniqueness

Uniqueness is to ensure that different requests have different CacheKeys. If they are the same, the cache will be disturbed.

Here’s an example:

A and B have two different requests. The CacheKey and cache mode are the same. The cache mode is READ_CACHE_FAILED_REQUEST_NETWORK.

In this case, A sends the request first, and the steps are as follows: Read cache fails – > Request network – > Write cache succeeds – > end.

Then B initiates a request. The steps are as follows: Read cache successfully – > End.

Since A and B have the same CacheKey, when B reads the cache, it reads A’s. Also, if B’s cache mode is REQUEST_NETWORK_FAILED_READ_CACHE, then if B’s request is successful, it will be overwritten by A’s cache because of the same CacheKey, and the next time A reads from B’s cache, it will be read from B’s cache. This is what I mean by cache mismatch.

4.4 Static

What is static? My definition of it is that if you send the same request multiple times, make sure the CacheKey is consistent, otherwise the cache is meaningless.

Imagine that we have a situation where each request is sent with the current timestamp, as follows:

RxHttp.get("/service/...")   
    .add("currentTime", System.currentTimeMillis()) 
    .setCache(CacheMode.READ_CACHE_FAILED_REQUEST_NETWORK) // The current request reads the cache first, fails, then requests the network
    .asString()    
    .subscribe(s -> {       
      // Successful callback
    }, throwable -> {       
      // Fail callback
    });
Copy the code

The above code, with a different timestamp each time it makes a request, will generate a different CacheKey internally each time it makes a request, and will write to the same cache without overwriting it, or more importantly, will fail each time it reads the cache. How can we avoid this? Yes, as described earlier, customizing a Cachekey solves this problem, but is there an easier way to do it? There are. Look down.

Exclude parameters that do not participate in the CacheKey grouping

We can call RxHttpPlugins. SetExcludeCacheKeys (String… Keys) method to set parameters that do not participate in the CacheKey grouping. For the above problem, you can do this:

RxHttpPlugins.setExcludeCacheKeys("currentTime")  // A variable parameter can be passed in multiple keys
Copy the code

At this point, when the request is sent, the current timestamp does not participate in the CacheKey grouping, thus ensuring that the CacheKey is the same every time the request is made. This is what I mean by statics

5, extension,

At this point, some people may ask me, I have a business scenario: open the app, the list first shows the cached data, and then request the network to pull the latest data, how to use RxHttp?

For this scenario, we need a caching pattern that reads the cache first and continues to request the network whether it succeeds or not. However, there is no such a mode in RxHttp’s 5 caching modes, what should we do? Now that we have mentioned it, there must be a way to achieve this scenario by combining the two modes, as follows:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
        // Send a request to read only the cache
        requestData(CacheMode.ONLY_CACHE);  
        // Then send a request to the network and write the cached request after success
        requestData(CacheMode.NETWORK_SUCCESS_WRITE_CACHE);
    }

    // Get the data
    public void requestData(CacheMode cacheMode) {
        RxHttp.get("/service/...")
            .setCacheMode(cacheMode)
            .asString()
            .as(RxLife.asOnMain(this))  // Perceive the life cycle and call back on the main thread
            .subscribe(s -> {
                // Successful callback
                if (cacheMode == CacheMode.ONLY_CACHE) {
                    // Cache read successfully callback
                } else if (cacheMode == CacheMode.NETWORK_SUCCESS_WRITE_CACHE) {
                    // Request a network success callback} }, throwable -> { }); }}Copy the code

In the above code, we send two requests, cachemode.only_cache and CacheMode.NETWORK_SUCCESS_WRITE_CACHE. Then, in the success callback, depending on the CacheMode, Perform different business logic.

So why doesn’t RxHttp add a caching pattern that directly supports this business scenario? There are two reasons why:

1. If directly supported, it would be necessary to add a cache read success callback, which would break the existing RxHttp code structure

2, personal feeling, the combination of the way, compared to adding a cache read successful callback, the code is more concise, learning cost is lower

Of course, for direct support, if you think of a better plan, we will definitely join, if you have a good plan, remember to share.

6, summary

In fact, the RxHttp caching is a 2-step process,

  • The first step is to set a cache storage directory and the cache maximum Size. If required, you can also set the global cache mode and cache duration
  • The second step is to set the corresponding cache mode when sending the request. If the global cache mode is used, this step can be skipped

Finally, if you like, please give this article a thumbs-up, if you can, please also give a star, creation is not easy, grateful. 🙏 🙏 🙏

The coroutine is on its way, it will take a little more time, please be patient.