RxJava Only want to call cache when api call is success - android

I am very new to RxJava and can't seem to find figure out the solution to this use case. I have been researching on this for 2 days now and no luck.
I have 2 Singles, remote and cache, to register a user in my app
I first call remote which saves the user data on a server, and returns a custom code to indicate successfully saved. I only want to call cache after I have checked the custom code from remote and gotten a success. If custom code comes as failure, I want to return that, and not go to the cache at all

The operator, which you're looking for, is flatMap. Example:
remoteApi.login().flatMap(new Function<String, SingleSource<String>>() {
#Override public SingleSource<String> apply(String response) throws Exception {
if (response.equals("success")) {
// do what you want to do with cache
return cache.save(response);
}
return Single.just(response);
}
}).subscribe(yourObserver);
Don't forget to use subscribeOn and observeOn...

Related

RxJava2 concat operator doesn't return appropriate sequence

When I make a request to the network, if an error occurs, then I will return data from the cache and the error. But sometimes I don’t get data from the cache, but I get only an error. The first time I launch the application, I always get only an error. If I call the getDashboard method once or several times, then everything is fine.
Here is a piece of code.
.onErrorResumeNext(throwable -> {
return Observable.concat(Observable.just(fromCache), Observable.error(throwable));
});
Full code here
https://gist.github.com/githubgist123/7e027675bb4db07fef606e23f39f8a96
Sorry, but the way you're doing your caching is wrong in my opinion. If you request twice consecutively and the cache is empty you'll end up with two network requests running and racing each other to write to the cache. You don't want that.
What you need is:
Observable.concat(
cache(),
network().share().doOnNext(setCache(...)).onErrorReturn(...)
.first()
)
You might think of synchronized your cache too or make it thread-safe by using the proper data structure.

How to make multiple calls with Retrofit?

I need to make multiple calls to API REST with Retrofit and show the response in a ListView, but I don't know how to do this and this code doesn't work.
Model
#GET("apks/{sha256}")
Call<DatoAPI> getTask2(#Path("sha256") String hash, #Query("Authorization") String key);
Implementation
for (String s: hash) {
Call<DatoAPI> call = services.getTask2(s, API.API_KEY);
call.enqueue(new Callback<DatoAPI>() {
#Override
public void onResponse(Call<DatoAPI> call, Response<DatoAPI> response) {
if (response.isSuccessful()) {
datoAPI = response.body();
items.add(datoAPI.getApp());
}
}
#Override
public void onFailure(Call<DatoAPI> call, Throwable t) {
Toast.makeText(getApplicationContext(),t.getMessage(),Toast.LENGTH_LONG).show();
}
});
}
Also I tried with call.execute() and same problem
I want to show this response in a ListView but it doesn't work.
First of all you need to understand the differences between Retrofit's Call#enqueue() and Call#execute() methods.
enqueue() method is Asynchronous which means you can move on to another task before it finishes
execute() method is Synchronous which means, you wait for it to finish before moving on to another task.
And in your case, you're using for loop to execute multiple requests in a single stretch.
Now, if you use for loops to execute network operation, the network operation will not stop for loops from going to the next iteration. Do not expect that the API will always respond in a fast enough way before going to for loops next iteration. That's a bad idea.
If you use Retrofit's execute() method, it will not allow you to continue to next line (or iteration) as its Synchronous behavior, plus it throws NetworkOnMainThreadException and IOException. Hence, you need to wrap the request in an AsyncTask and handle IOException.
I'd recommend you to use RxAndroid with RxJava instead of using for loops. There are plenty of tutorials out there on this topic.
Refer to the following StackOverflow questions to solve your problem.
How to make multiple request and wait until data is come from all the requests in Retrofit 2.0 - Android?
Asynchronous vs synchronous execution, what does it really mean?
Adjust the code as per your requirements.
Good luck!

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
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)
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

RxJava network requests and caching

I am seeking an example of a flow I'm trying to implement with help of RxJava.
Suppose I want to show a list of data. The flow should look something like this:
Read cache. If it contains the data, show it;
Send an API request to the server:
If it returned the data, then cache it and show it.
If it returned and error and there was no cached data, then show an error.
If it returned and error and there was something cached, then do nothing.
Right now I have a method that does something similar (with lots of inspiration from Jake's u2020). The main difference is that it uses in-memory caching, which means there's no need for a separate Observable for reading from cache and it can be done synchronously.
I don't know how to combine two observables (one for reading from cache and the other for API call) and obtain the flow described above.
Any suggestions?
I think I solved my problem. The observable chain looks like so:
apiCall()
.map(data -> dataInMemory = data)
.onErrorResumeNext(t -> data == null ?
Observable.just(Data.empty()) : Observable.empty())
.startWith(readDataFromCache().map(data -> dataInMemory = data))
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(dataRequest);
The main point is, that if readDataFromCache() throws an error, it will call onCompleted() without calling onError(). So it should be a custom Observable which you can control.
Data.empty() is a stub for my data - the Subscriber should treat it as an error.
dataInMemory is a member in my controller which acts as in-memory cache.
EDIT: the solution doesn't work properly. The completion of one use case (see comment) is not achieved.
EDIT 2: well, the solution does work properly after some tweaking. The fix was returning different types of observables depending on the state of in-memory cache. Kind of dirty.
Here is my solution:
readDataFromCache().defaultIfEmpty(null)
.flatMap(new Func1<Data, Observable<Data>>() {
#Override
public Observable<Data> call(final Data data) {
if (data == null) {
// no cache, show the data from network or throw an error
return apiCall();
} else {
return Observable.concat(
Observable.just(data),
// something cached, show the data from network or do nothing.
apiCall().onErrorResumeNext(Observable.<Data>empty()));
}
}
});
I don't add the subscribeOn and observeOn because I'm not sure readDataFromCache() should use ioScheduler or uiScheduler.

Android Volley ImageListener onResponse behaviour

I'm using Android Volley Library and my question should be easy to answer, if you know it ;).
I need to know how com.android.volley.toolbox.ImageLoader.ImageListener exactly behaves when
handling successful responses. The docs says
The call flow is this: 1. Upon being attached to a request,
onResponse(response, true) will be invoked to reflect any cached data
that was already available. If the data was available, response.getBitmap() will
be non-null. 2. After a network response returns, only one of the following
cases will happen: - onResponse(response, false) will be called if the
image was loaded. or - onErrorResponse will be called if there was an error
loading the image.
What I want to know is this: does that mean that I can have onResponse called twice (first with isImmediate set to false and then set to true)? Can I rely on that? I mean will it always be like this (if the image loading is succesful)?
I'm trying to do something like this
imageLoader.get(image.getUrl(), new ImageListener() {
#Override
public void onErrorResponse(VolleyError error) {
callback.call(null, error);
}
#Override
public void onResponse(ImageContainer response,
boolean isImmediate) {
if (response.getBitmap() != null) {
callback.call(response.getBitmap(), null);
}
}
});
I need callback.call() to be called when the image could be loaded succesfully and I also need response.getBitmap() to return the actual bitmap and not null.
Thanks in advance!
What I want to know is this: does that mean that I can have onResponse called twice (first with isImmediate set to false and then set to true)? Can I rely on that? I mean will it always be like this (if the image loading is succesful)?
Short answer: yes.
Unless the implementation of this class is changed (and I highly doubt that will happen), you can rely on this call order being respected.
If it helps, I gave some detail on the three possible response cases in another answer here: https://stackoverflow.com/a/32464875/3227795

Categories

Resources