Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrofit 2 load cache before network call

i am using retrofit 2 to make my API calls, but my issue is that API response takes time to show the response, is there any chance I can store the data and load that cache and show it and then meanwhile calling the network API.

Like for example :

the first hit -> Make Network Call - render the output to the screen say a list view --> store the response in the cache

next time the user comes on screen --> load the cache and render it to screen --> make the network call --> refresh the adapter with changes

am referring to one of a gist link https://gist.github.com/Tetr4/d10c5df0ad9218f967e0

like image 359
Peter Avatar asked Oct 21 '17 12:10

Peter


2 Answers

Yes, there are many solutions for doing that.

This is the solution if you use RxJava (which works well with retrofit)

First is using concat like:

Observable<Data> source = Observable  
  .concat(memory, disk, network).first();

memory, disk, and network are Observables. It will take first. That means if the cache is available, use cache, else disk, else network.

The problem in that is that it will never go to the network once it has a cache. That's why you should do something like that:

 getRemoteItems().publish { Flowable.merge(it,  getLocalItems().takeUntil(it)) }

That will get the remote and local at the same time, but stop the local one once the remote data has been fetched. You can use doOnSuccess to fill the local database with the remote data using

Yes, there are many solutions for doing that.

First is using concat like:

Observable<Data> source = Observable  
  .concat(memory, disk, network).first();

memory, disk, and network are Observables. It will take first. That means if the cache is available, use cache, else disk, else network.

The problem in that is that it will never go to network once it has a cache. That's why you should do something like that:

 getRemoteItems()
        .doOnSuccess { storeToLocal(it) }
        .publish { Flowable.merge(it,  getLocalItems().takeUntil(it)) }

That means that it gets the remote items to the same time as it tries to get the cache. If it was able to fetch remote data it stores the data to the cache.

If you're using the first example without first() it will deliver in the worst case 3 times Data but wait until the observable before is finished. That means it will try memory, once memory call onComplete() it goes to disk. If the disk is completed it try the network.

Observable<Data> source = Observable  
      .concat(memory, disk, network) 
like image 110
Emanuel S Avatar answered Nov 09 '22 17:11

Emanuel S


You could first cache all the responses:

public static Retrofit getAdapter(Context context, String baseUrl) {
    OkHttpClient.Builder okHttpClient = new OkHttpClient().newBuilder();
    Cache cache = new Cache(getCacheDir(), cacheSize);

    okHttpClient.cache(cache).build();

    Retrofit.Builder retrofitBuilder = new Retrofit.Builder();
    retrofitBuilder.baseUrl(baseUrl).client(okHttpClient);
    return retrofitBuilder.build();
}

And in the next network call, you could check if the response has changed. If so, then you get that new response, if not then you don't need to refresh your adapter:

if (response.isSuccessful() &&
    response.raw().networkResponse() != null &&
    response.raw().networkResponse().code() ==
       HttpURLConnection.HTTP_NOT_MODIFIED) {
    // the response hasn't changed, so you do not need to do anything
    return;
}
// otherwise, you can get the new response and refresh your adapter
...

Following @vsync comment, I just updated this answer. I hope it's better now.


References: https://android.jlelse.eu/reducing-your-networking-footprint-with-okhttp-etags-and-if-modified-since-b598b8dd81a1

like image 45
Rodrigo Rosatti Galvão Avatar answered Nov 09 '22 18:11

Rodrigo Rosatti Galvão