Rxjava, how to block until it finishes? - android

So I have 3 rest services to call, problem is, I need services 1 to finish before calling service 2 and I need service 2 to finish before calling service 3.
Because each time I need to pass data from the previous service to the next.
right now I'm chaining my tasks but I don't like it because method naming becomes quite massive
Example of service 1 ;
compositeDisposable.add(simpleRetrofitService.getInventaireDisponibleResultatDtos()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(this::callService2Then3ThenSaveThenGoToNextScreen,
this::logErrorAndDisplayPopup));
callServiceOneThenTwoThenSaveThenGoToNextScreen()
then define an other asynctask which calls callService3ThenSaveThenFoToNextScreen()
Any ideas?
Thanks.
Edit : precision
Call1() will return an object containing a list of integer like [1, 2, 8, 132]
Then I have to call call2() for each integer.
And same thing for call3(), call2() gives me some values and I have to call Call3() for each values.

For simplicity, I'll assume your calls are simpleRetrofitService.call1(). simpleRetrofitService.call2() and simpleRetrofitService.call3(). I will also assume they are returning http://reactivex.io/RxJava/javadoc/io/reactivex/Single.html
Then you can do some basic stuff like this (using http://reactivex.io/documentation/operators/flatmap.html):
simpleRetrofitService.call1()
.flatMap(r1 -> simpleRetrofitService.call2(r1))
.flatMap(r2 -> simpleRetrofitService.call3(r2))
.subscribeOn(...)
.observeOn(...)
.subscribe(...)
You don't really need AsyncTask at all.
Update as a response to question update:
Assuming your call returns list, you can do something like this (using http://reactivex.io/RxJava/javadoc/io/reactivex/Single.html#flattenAsObservable(io.reactivex.functions.Function)):
simpleRetorfitService.call1()
.flattenAsObservable(...)
.flatMap(listItem -> simpleRetrofitService.call2(listItem)
...

Related

How to keep track of the number of emits in flowable?

Let's say I have a flowable, that some view is subscribed to and it's listening to the changes. I would like to add a custom method based on only the first emit of the flowable, but also keeping the other methods that listen to the changes. What is the best way to approach it?
The naive approach I have is to duplicate the flowable and convert it to Single or Completable to get the results, but it seems redundant.
Thank you.
Use .take(1). BTW also make sure that flowable is shared (otherwise some observers will miss events).
I think you can use share operator for that. Share operator makes a Connectable Observable. And then Connectable Observable publishes items each subscribes.
val o = Flowable.fromArray(1, 2, 3, 4, 5)
.map {
println("heavy operation")
it + it
}
.share() // publish the changes
.subscribeOn(Schedulers.computation()) // for testing. change what you want
o.take(1).subscribe { println("Special work: $it") } // take one
o.subscribe { println("Normal work: $it") }
Result
heavy operation
Special work: 2
Normal work: 2
heavy operation
Normal work: 4
heavy operation
Normal work: 6
heavy operation
Normal work: 8
heavy operation
Normal work: 10

Starting an asynchronous operation using RxJava without subscribing to an observable?

Let's say your DAO has this method that updates user records in the DB:
#Update
fun update(user: User): Single<Int>
Recently I started learning RxJava and so far I have seen lots examples like following:
// Example 1
disposable.add(dao.updateUser(user)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
Log.d(TAG, "response received")
}
In the above example, I understand that as soon as the subscription starts, updateUser() will be executed on a worker thread and the subscriber will be notified and run in the main thread once the execution completes.
But what if you are not interested in the result of updateUser(), and all you want is just to execute updateUser() on a worker thread?
So far I have tried doing:
// Example 2
dao.updateUser(user)
or
// Example 3
dao.updateUser(user).subscribeOn(Schedulers.io())
But they didn't work. It seems the update requests are never executed, nothing was logged and records didn't change. I am guessing that's because there isn't any subscriber attached to it.
For now I am forcing it to work by attaching a random subscriber that doesn't really do anything like the one in Example 1. One of the problems with the approach is that I might need to make this request a lot and that might create a lot of dummy subscribers, not to mention that the code looks really bad.
Could you help me find a better way of handling this?
But You already wrote answer for Your question.
You can just call:
dao.updateUser(user).subscribe()
If You want manipulate / jump between thread you are doing something like in Example 1.

Periodically call an observable and switch to next one only when next one succeeds

I need to poll endpoint every second, currently I do it with
Observable.interval(0, 1, TimeUnit.SECONDS, ioScheduler)
.switchMap { return pollWithRetrofit() }
It works fine except the situation when the calls start taking more than 1 second to process, so the retrofit subscription is cancelled by swithMap before I get any response. It can happen multiple times in a row, effectively leaving the client without any response from the poll calls for long duration. In this case I would like to not cancel the retrofit call until I get a response from the next call.
I know that switchMap cancels the previous subscription when the base subscription produces onNext call, currently it happens every second by the Observable.interval, so my idea is to cancel previous call only when the retrofit calls it's onNext, i.e moving the switching one step forward the reactive chain.
How do I do that? Or is there some other solution?
You could use onBackpressureDrop and flatMap with maxConcurrency of 1 to make sure a longer call is still allowed to succeed:
Flowable.interval(0, 1, TimeUnit.SECONDS, ioScheduler)
.onBackpressureDrop()
.flatMap(v -> pollWithRetrofit(), 1);
fwiw I'm using code like following for doing something similar...am not fully convinced that this is cleanest approach either (though has been working successfully for a while now)
someRetrofitInterface.apiCall()
.repeatWhen { completed -> completed.delay(30, TimeUnit.SECONDS) }
.retry(3)
(from https://github.com/joreilly/galway-bus-android/blob/master/base/src/main/java/com/surrus/galwaybus/domain/interactor/GetNearestBusStopsUseCase.kt)

Combine 2 observables and get output from one which complete first

I have a subscription that wait for the push notification and another one that is polling the server to get response. I want to start both observable together and return the data from the one which finish first. What would be operator to use here?
Since you want to have the data of the first one to finish, you have to put the data somewhere until you get to the terminal event by collecting each into its own list and using amb that picks the source that signals an event (the collected list) first. Then you can unroll the list back to individual items.
Observable<A> source1 = ...
Observable<A> source2 = ...
Observable.amb(source1.toList(), source2.toList())
.flatMapIterable(list -> list)
.subscribe(...);
The operator you are looking for is first. Of-course, you'll have to merge the Observables first (by using merge, or probably better - mergeDelayError, so if only one of them fails, you'll still get the first which finishes with a vaild result).
Should look like:
Observable.mergeDelayError(pushObservable, pullObservable)
.first()
.subscribe(data->...);

Queue of requests with RxJava

I need to implement synchronous calls with RxJava and Retrofit.I have an ArrayList of ids. I need to iterate this array and make the call to the web server for each id using Retrofit but I know how to do this only async, could U tell me how to do this like in queue when after one call finished the next one starts.
Your question is worded quite ambiguous, but I think I might have understood it. Starting from a list of items you can create an observable of these with from(). This values can be mapped afterwards your API calls. Using concatMap() guarantees the order of your results, so you effectively get an observable over your results. Which these you can do whatever you want, even call toBlocking() on it and make the observable synchronous. But there should not be any need for this.
List<Result> results =
Observable.from(ids)
.concatMap(id -> callToWebServer(id))
.toList()
.toBlocking()
.single();
This code will execute them synchronious
Observable.from(ids)
.map(id -> callToWebServer(id).toBlocking().first())
But you need to handle all network errors from callToWebServer() carefully in map().

Categories

Resources