I have two different endpoints I am using to fetch data for a user. I am using retrofit with the RX Adapter factory. If I make a call to both the endpoints inside a single method sequentially is it considered to be a parallel call executing on two different threads. If not how could I make these API calls parallel using RX? or a way to get the response at the same time while fetching the data in parallel. for example, the first endpoint could take 5 seconds while the second takes 7 seconds but the end response would be available after 7 seconds.
fun fetchData() {
api.getData()
.subscribeOn(Schedulers.io())
.subscribe(
{ profileResponse ->
//ProfileResponse Object
Timber.d("profileResponse: $profileResponse")
//store response for later use
Cache.save("key", profileResponse.toString())
},
{
Timber.e("error")
}
)
api2.getData()
.subscribeOn(Schedulers.io())
.subscribe(
{ profileDetails ->
//profileDetails Object
Timber.d("profileDetails: $profileDetails")
},
{
Timber.e("error")
}
)
}
Firstly, you're using subscribeOn() for each observable so it's already executing in parallel.
Is there a way to get the response at the same time while fetching the data in parallel. for example, the first endpoint could take 5 seconds while the second takes 7 seconds but the end response would be available after 7 seconds.
For this, you can use Observable.zip like the following where the time required is the
maximum of two calls:
val disposable = Observable.zip(
firstNetworkCall().subscribeOn(Schedulers.io()),
secondNetworkCall().subscribeOn(Schedulers.io()),
BiFunction{
firstResonse: ResponseOneType,
secondResponse: ResponseTwoType ->
combineResult(firstResponse, secondResponse) }))
.observeOn(AndroidSchedulers.mainThread())
.subscribe { it -> doSomethingWithIndividualResponse(it) }
This article may help to visualize how it's working underneath.
If I make a call to both the endpoints inside a single method sequentially is it considered to be a parallel call executing on two different threads. If not how could I make these API calls parallel using RX?
They are parallel. You're subscribing to a an observable on an IO scheduler and not blocking waiting for responses.
or a way to get the response at the same time while fetching the data in parallel. for example, the first endpoint could take 5 seconds while the second takes 7 seconds but the end response would be available after 7 seconds.
One way is to use zip() to combine your observables to a single observable that emits when all the sources have emitted.
Related
I am very new to using RxJava with Retrofit in Android. I have successfully written the API calls and developed the interface too. Now, I want to write my code in a way that I can send two requests: second request depending upon the values of first request. Can someone guide me if this is possible? If so then how? Any code snippet will really be helpful.
For example: following are two requests:
mCompositeDisposable.add(fcService.getStationList()
.subscribeOn(Schedulers.io()) // "work" on io thread
.observeOn(AndroidSchedulers.mainThread()) // "listen" on UIThread
.subscribe(this::handleResults, this::handleError)
);
mCompositeDisposable.add(fcService.getStationSensor("12345678")
.subscribeOn(Schedulers.io()) // "work" on io thread
.observeOn(AndroidSchedulers.mainThread()) // "listen" on UIThread
.subscribe(this::handleResults, this::handleError)
);
Second request is possible with the value from the first request's response. Is it possible to merge these two requests in a way that I write code only once for them?
With the flatMap operator you can check the response of the first call and choose the next action to follow, in this way you build a new Observable that you can subscribe to (The next "code" is kotlin style):
Single<StationSensor> newSingle =
fcService.getStationList().flatMap{ stationList ->
when(stationList){
"OK_value" -> fcService.getStationSensor(stationList)
else -> Single.error(RuntimeException("Error response"))
}
}
I am trying to implement an Android app which needs to obtain a big amount of data from a backend service and save it to a db to later work on it.
The below code describes the process:
itemsService
.getAllItemIds() //This returns Single<List<Int>> from backend
.subscribeOn(Schedulers.io())
.subscribe({
Observable.fromIterable(it)
.map({
itemsService
.getItemById(it) //This gets one item details from backend
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
//Add item details to db
}, {
//Some error
})
})
}, {
//Some error
})
I obtain a list of ids and then map each of these ids to a network call to obtain the full object.
This works for a test set of, say, 10 items, but the production set contains over 50 000 ids. It works initially, saving the items, but around 5-10% it grinds to a halt and the app dies.
I assume the reason here would be that Rx keeps the reference between the source and the mapped value.
My question is: is there a way to "pool" the source emissions to, let's say, 10 at a time? Or maybe there is some other mechanism I am not aware of?
You didn't mention what exactly "grinds to a halt" means, but it makes sense that you will get out of memory in real case of 50,000 items, cause you will basically try to create 50,000 threads at once to fetch each items details.
moreover, instead of chaining Observables using operators, you're creating nested chains at subscribe/map, you can read here why you shouldn't.
regarding limiting the work to 10 at a time, there is an flatMap overload for that, at the end it might look something like this:
itemsService
.getAllItemIds() //This returns List<Int> from backend
.flatMapIterable { t -> t }
.flatMap({
itemsService
.getItemById(it) //This gets one item details from backend
.subscribeOn(Schedulers.io())
}, 10) //limit flat map parallelism by desired value
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
//Add item details to db
}, {
//Some error
})
I have to schedule same observable repeatedly for N number of times with M seconds delay between each observable:
O1____1sec____O2____1sec____O3____1sec____O4(completed)
Notice no delay between start and ending observable,
Observable<Precious> result = <~> // hidden for brevity (it's just a long time consuming observable that can take suppose up to 10 seconds or more)
Observable.timer(M,TimeUnit.SECONDS).compose(x -> result).take(N).subscribe();
Problem Here is result observable that is doing expensive network calls, will it timout itself after timer expires , or we have to tell it to do so , if so how?
You can use the combination of concatMap to concat the observables, and Delay to delay the emission of every one
/**
* Another elegant solution it would be to create an observable with the list of items, and then use
* concatMap to pass all items from the first observable to the second, then this second observable
* can be created used delay operator afterwards.
*/
#Test
public void delayObservablesWithConcatMap() {
Observable.from(Arrays.asList(Observable.just(1), Observable.just(2),Observable.just(3)))
.concatMap(s -> s.delay(100, TimeUnit.MILLISECONDS))
.subscribe(n -> System.out.println(n + " just emitted..."),
e -> {
},
() -> System.out.println("Everybody emitt!"));
new TestSubscriber().awaitTerminalEvent(1000, TimeUnit.MILLISECONDS);
}
You can see more examples of delay here https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/utils/ObservableDelay.java
To space out items from obsevable source so that they're emitted every N seconds, use the following pattern:
source.zipWith(Observable.interval(1, TimeUnit.SECONDS), (src, dummy) -> src)
Caveat here is that if your source observable takes more time than the interval, then the items get queued up.
Now that I've re-read your question and clarifications, I think what you need is this:
Observable.interval(1, TimeUnit.SECONDS)
.switchMap(dummy -> result)
This will unsubscribe and resubscribe to the result observable every 1 second. It will only workif your result observable cancels network calls on unsubscription.
I want to asynchronously retrieve data via multiple REST APIs. I'm using Retrofit on Android with the rxJava extension, i.e. I execute any GET request by subscribing to an Observable.
As I said, I have multiple source APIs, so when the first source does not yield the desired result I want to try the next on, if that also fails, again try the next and so forth, until all sources have been queried or a result was found.
I'm struggling to translate this approach into proper use of Observables since I don't know which operators can achieve this behaviour and there are also some constraints to honor:
when a result has been found, the remaining APIs, if any, should not be queried
other components depend on the result of the query, I want them to get an Observable when starting the request, so this Observable can notify them of the completion of the request
I need to keep a reference to aforementioned Observable because the same request could possibly be made more than once before it has finished, in that case I only start it the first time it is wanted and subsequent requests only get the Observable which notifies when the request finished
I was starting out with only one API to query and used the following for the request and subsequent notification of dependent components:
private Observable<String> loadData(int jobId) {
final ConnectableObservable<String> result = Async
.fromCallable(() -> getResult(jobId))
.publish();
getRestRequest()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
dataHolder -> {
if (dataHolder.getData() != null && !dataHolder.getData().isEmpty()) {
saveData(dataHolder.getData());
} else {
markNotFound(dataHolder);
}
},
error -> currentJobs.remove(jobId),
() -> {
currentJobs.remove(jobId);
result.connect();
});
return result;
}
This code was only called for the first request, the returned Observable result would then be saved in currentJobs and subsequent requests would only get the Observable without triggering the request again.
Any help is highly appreciated.
Assuming you have a set of observables that re-connect each time you subscribe:
List<Observable<Result>> suppliers = ...
Then you just need to do the logical thing:
Observable<Result> results = Observable
.from(suppliers)
.concatMap(supplier -> supplier)
.takeFirst(result -> isAcceptable(result))
.cache()
Use .onErrorResumeNext, and assuming that each service observable may return 0 or 1 elements use first to emit an error if no elements are emitted:
Observable<T> a, b, c;
...
a.first().onErrorResumeNext(t -> b.first())
.onErrorResumeNext(t -> c.first())
.onErrorResumeNext(t -> d.first())
...
I am playing around with RxAndroid. I have a List of Observables all of which are api requests (using Retrofit). I want to fire one of them every x seconds or milliseconds but then zip the responses together. I seems that once I subscribe to Observable.zip(requests, someFunction) all of them are fired off at once. Any tips?
Thanks!
EDIT: looks like adding delaySubscription to each request maybe the answer
You are looking for either delay() or delaySubscription().
delay() will delay the result of the Observable being published to the subscriber.
delaySubscription() will delay subscription to the Observable.
Observable.zip(someObservable.delaySubscription(100, TimeUnit.MILLISECONDS),
someOtherObservable.delaySubscription(200, TimeUnit.MILLISECONDS),
someThirdObservable.delaySubscription(300, TimeUnit.MILLISECONDS),
new Func3<Object, Object, Object, Void>() {
...
}).subscribe();
Also, it's posible to achieve a periodical sending effect by using the interval() operator.
Let's see a simple example. Imagine you have an array, numbers, whose values have to emitted each x time. You could create an Observable that emits them:
Observable<Integer> values = Observable.from(numbers);
And now, another Observable that emits each (for instance) 30 milliseconds:
Observable<Long> interval = Observable.interval(30, TimeUnit.MILLISECONDS);
So, through the zip() operator you could combine both to achieve the periodical emission of the values in your number array:
Observable.zip(values, interval, (arrayElement, aLong) -> arrayElement)
.subscribe(arrayElement -> doSomething(arrayElement));
I used it to get an animation effect for a progress indicator. I wrote a complete example project you can check in github.