RxJava - How can I set thread for observeOn dynamically? - android

Let's say I have following code.
Scheduler ioSC = getIOThread();
Scheduler mainSC = AndroidSchedulers.mainThread();
Subscription subs = getObservable()
.doOnNext(getAction1())
.doOnSubscribe(getAction2())
.observeOn(/****TODO***/)
.subscribe(getSubsAction());
In this code, I want to be able to set the thread for observeOn() based on the type if item I get from getObservable().
How can I add this condition checking in this subscription?
Is it even possible to dynamically set a thread for observeOn()?
Thanks!

You could use flatMap() for this:
getObservable()
.doOnNext(getAction1())
.doOnSubscribe(getAction2())
.flatMap(item -> Observable.just(item).observeOn(getScheduler(item)))
.subscribe(getSubsAction());
But then the question comes up, why would you handle different items on different threads using the same subscriber. This seems a little off and you might want to reconsider your chain.

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

RxJava2 and Android complex observable chaining

I have been working with Rx Java 2 for awhile but recently came across a situation that has stumped me. I have a semi-complex chain of operations and wish to pass a "state object" down the chain.
There are 4 operations during which I wish to repeat operations 2 and 3 (serialy, not together) until certain conditions are true. I know i can solve this by chaining each operation using andThen(), but this limits my ability to pass a state object down the chain without reaching outside of the chain.
The reason I need to have a state object is because I need to save an initial value during the first operation and compare it to a value recieved during operation 4 to determine if the overall procedure was successful.
Any clues as to what RxJava2 operators can help me achieve the proper repeat conditions for operation 2 and 3? I would prefer to not nest observables if possible.
You can keep your state as some AtomicReference<State> and use repeatUntil operator.
AtomicReference<State> state = new AtomicReference<>();
Completable operation = Completable.create() // do something and modify state
.repeatUntil(() -> state.get() == SATISFYING_CONDITION);
You can easily chain these Completables with andThen

RxJava1 concatMap cause MissingBackpressureException

I am trying to transform Observable using concatMap, since the order is important for my case.
#Test
fun load_data() {
val sub = TestSubscriber<Long>()
var s = BehaviorSubject.create<Long>()
s.concatMap {
Observable.timer(it, TimeUnit.MILLISECONDS)
}
.take(4)
.subscribe(sub)
s.onNext(5)
s.onNext(6)
s.onNext(7)
s.onNext(8) //rx.exceptions.MissingBackpressureException
sub.awaitTerminalEvent(500, TimeUnit.MILLISECONDS)
sub.assertNoErrors()
}
I have changed real data loading to Observable.timer() in order to simplify example and make it easier to reproduce.
I am using in the app BehaviorSubject to link UI actions with rx
From documentation, especially from marble diagram I expect that it will store items in queue and transform them one-by-one.
However it seems like concatMap has queue with size set only to 2 items. Adding more items cause MissingBackpressureException
So I have following questions:
Why concatMap has queue size 2 instead of RxRingBuffer.SIZE as
other operators has?
Should I as a rule add any of onBackpressure* operators before
calling concatMap to prevent from MissingBackpressureException
exception?
Before I answer the questions, please consider switching to RxJava 2 where this is be not a problem with an Observable.
Why concatMap has queue size 2 instead of RxRingBuffer.SIZE as other operators has?
The operator runs one Observable at a time and there was no reason to prefetch more than 1 in advance.
Should I as a rule add any of onBackpressure* operators before calling concatMap to prevent from MissingBackpressureException exception?
Yes.

How to use RxJava to decrease the rate of events generated by button's tap?

One of the tests executed by QA team is frenetically tapping on some button to make sure that only one event will be executed. Is it possible to use RxJava for that ensure this behavior? I've tried reading this backpressure page from RxJava but I found that really hard to understand. Also, I don't see how I would emit an event from button's onClickListener - everything I think ends up creating a new Observer instead of emitting a new event.
The simplest way for achieve it, is to throttle the events. You can use the throttleFirst operator: you basically define a window in which only one event will be triggered.
If you use RxBinding library you can create a stream from your click, and you could have something like:
RxView.clicks(button).throttleFirst(600, TimeUnit.MILLISECONDS)
.subscribe(empty -> { // your action});
So, after you click on your button, for 600 milliseconds any other clicks will do nothing, and you can prevent a double action.
EDIT: If you can't use RxBinding you can create your own Observable in this way:
Observable<View> clickEventObservable = Observable.create(new Observable.OnSubscribe<View>() {
#Override
public void call(final Subscriber<? super View> subscriber) {
mButton.setOnClickListener(v -> {
if (subscriber.isUnsubscribed()) return;
subscriber.onNext(v);
});
}
});
NOTE: This implementation is for RxJava 1, but you should avoid it (you should not use create for creating an Observable in RxJava 1), but you can use RxJava 2, where using create is totally fine.
I'd use a mix of debounce() to only fire after the user is finished with its input. Then only take the first() event and keep subscribed with repeat(). The last two together will ensure that your click event was handled before another one can be observed.
RxView.clicks(button)
.debounce(300, TimeUnit.MILLISECONDS)
.first()
.repeat()
.subscribe();
You just have to take care of unsubscribing from this Observable by yourself, maybe using repeatWhen() instead, because completion won't unsubscribe you anymore.
For that you don't need to use RxJava, you can simply use instance variable as an indicator, using that you can check whether action is in progress for previous click and if so just ignore events.

Trigger Observable on Subject's onNext and Share Result

I have a button which when pressed should make the btnSubject's onNext fire and make an API call in an Observable created in my ViewModel like so:
val apiObservable =
btnSubject.flatMap{ apiService.getSomething()
.toResponseOrErrorObservable()
.subscribeOn(Schedulers.io())}
Then I can reuse this observable to create two more, which are then subscribed to from my view allowing me to keep the logic in my ViewModel like so:
val successObservable = apiObservable.filter{ it.isSuccess() }
val failureObservable = apiObservable.filter{ it.isFailure() }
So apiObservable is triggered by the btnSubject.onNext() being called.
The view is then updated because it's listening to the successObservable and failureObservable
Is this possible? Perhaps with a .share() on the apiObservable?
UPDATE
I added the share operator and all observables emitted items when first subscribing. Even the filters didn't stop it... I must be missing something obvious
There might be a few way to do that.
As you have written, using share() operator multiplies output to many Subscribers. However, you have to be careful, that you also have to call connect() to turn cold Observable into hot one. If calling also replay(), you woudln't need to call connect() many times.
(Source)
However, there is more simple solution: use Jake Wharton's library RxReplayingShare. The author of previous blog post suggests it in his next article

Categories

Resources