Updating flatMap concurrent limit in the same subscription - android

I have an Android service that downloads files when a PublishSubject receives download events through EventBus and I want to limit the number of concurrent downloads based on a setting.
When the service is instantiated, it creates the PublishSubject and the following subscription:
PublishSubject<DownloadEvent> downloadsSubject = PublishSubject.create();
Subscription downloadSubscription = downloadsSubject
.subscribeOn(Schedulers.io())
.filter(event -> !isDownloaded(event))
.flatMap(this::addDownloadToQueue)
.flatMap(this::startDownload, preferences.getDownloadThreadsNumber())
.onBackpressureBuffer()
.subscribe();
But the setting is obtained only when the subscription is made, and changes to the setting have no effect.
Is there a way to update this value (or another approach) for next queue emissions without having to subscribe again?

Here is a runnable class with a custom operator which should do what you wanted.
There are several race conditions in such scenarios and I've tried to cover most of them. The operator doesn't coordinate backpressure so you may need onBackpressureBuffer.

Related

Ensure sequential state update when using RXJava scan operator

I'm trying to implement redux state update pattern using RXJava
val subject=PublishSubject.create()
val subject1=PublishSubject.create()
// multiple threads posting
// on subject and subject1 here. Concurrently
subject.mergeWith(subject1)
.scan(
getInitState(),
{state, event ->
// state update here
}
)
.subscribe({state ->
// use state here
})
As you can see, I'm using scan operator to maintain the state.
How can I be sure that the state updates happen sequentially even when multiple threads are producing events?
Is there some mechanism in scan operator which makes the events stand in some queue while waiting for current state update function to finish?
What I have done:
I have successfully implemented this pattern in Android environment. It's really easy because if you always do the state update in
AndroidSchedulers.mainThread()
And make state object immutable you are guaranteed to have atomic and sequential state update. But what happens if you don't have dedicated scheduler for state updates? What if you are not on Android?
What I have researched:
I have read the source code for scan operator and there is no
waiting "queue" involved. Just simple state update and emission
I have also read SerializedSubject source code. There indeed is a waiting queue which serializes emissions. But what happens if I have two subjects? Serializing both of them doesn't mean that they don't interfere with each other.
To force execution on a single thread, you can explicitly create a single thread scheduler to replace AndroidSchedulers.mainThread():
val singleThreadScheduler = Schedulers.single()
Even if the events are emitted on other threads, you can ensure you process them only on your single thread using observeOn:
subject.mergeWith(subject1)
.observeOn(singleThreadScheduler)
.scan(
getInitState(),
{state, event ->
// state update here
}
)
.subscribe({state ->
// use state here
})
The difference between observeOn and subscribeOn can be pretty confusing, and logging the thread id can be useful to check everything is running on the thread you expect.
http://reactivex.io/documentation/scheduler.html

RxJava - How can I set thread for observeOn dynamically?

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.

Perform doOn<...> even if unsubscribed

I have an Observable that can emit events in some time after subscribing (e.g. an Observable from the Retrofit). The subscription to this Observable is strictly View-related, so when the View is destroyed I'm unsubscribing from the Observable. I want to perform some actions in doOnNext even if I unsubscribe the Subscription.
Example code:
final Observable<String> observable = ...; // will emit an event in some time in future
final Subscription subscription =
observable.doOnNext(new Action1<String>() {
#Override
public void call(String s) {
//this should be called even if the subscription is unsubscribed
}
}).subscribe();
subscription.unsubscribe();
Is there a way to make sure doOn<something> will be called even if the Subscription is unsubscribed?
EDIT:
Let me give you a bit clearer example:
final Observable<List<GithubRepo>> observable = getGithubReposFromApi();
subscription = observable
.doOnNext(githubRepos -> cacheGithubReposInDb(githubRepos))
.subscribe(githubRepos -> displayGithubReposInCurrentActivity(githubRepos));
And in Activity's onDestroy:
subscription.unsubscribe();
Now... If githubRepos were received after the Activity has been destroyed, the result wouldn't be cached in the database. And I would like it to be.
Okay, so you want the computation to run but want to cut off the Activity-dependent subscriber. You can publish() the sequence and subscribe to it. When unsubscribed, the original sequence will still go on:
ConnectableObservable<T> co = observable
.doOnNext(githubRepos -> cacheGithubReposInDb(githubRepos))
.publish();
Subscription s = co.subscribe(githubRepos ->
displayGithubReposInCurrentActivity(githubRepos));
co.connect();
// ...
s.unsubscribe();
I think you could do one of two things:
1.) Just have two Subscribers: one for View related stuff and one for the other side-effects that you are currently handling in doOnNext. To still have only one upstream subscription you could use:
Observable sharedObservable = observable.replay().refCount();
or maybe just
Observable sharedObservable = observable.share();
// which is the same as observable.publish().refCount();
2.) Use
Observable observableThatWillNeverTrulyUnsubscribe = observable.doOnNext(/* your doOnNext */).publish().autoConnect();
Now, when your Subscriber subscribes to observableThatWillNeverTrulyUnsubscribe it will subscribe to observable and will start emitting items. However, when the Subscriber unsubscribes, it will not unsubscribe upwards and thus doOnNext will continue to receive items.
I think I would prefer to first option as with the second you are giving up any possibility of ever stopping the work of observable.

RxJava Observable based on items on a list

I need an Observable that never ends, and just process some data and chain another observable when there are items on a list. Is there any way of accomplish that, and what would be the best approach=?
My closest idea was to create a timer observable and check every x seconds if there are items on the list. This idea is not ideal, because i need to process the data as soon as there are values on that list, which i modify outside the observable chain.
return Observable.timer(2, TimeUnit.SECONDS)
.flatMap(integer -> captureList.getLatestCaptureCut())
.flatMap(vp::processVideo)
.observeOn(AndroidSchedulers.mainThread())
.repeat()
I think you can use Subject, and push your next items there.
PublishSubject<Integer> subject = PublishSubject.<Integer>create();
subject.flatMap(integer -> captureList.getLatestCaptureCut())
.flatMap(vp::processVideo)
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
//push new items
subject.onNext(0);
subject.onNext(1);
I would suggest a PublishSubject in your CaptureList class. Instead of providing a pull method getLatestCaptureCut(), you could provide a push method, with a Subject:
PublishSubject<VP> captured = PublishSubject.create();
You could then .subscribe() to the PublishSubject and process the data when they come in.
In your CaptureList you would call
captured.onNext(vp);
every time new data is available. For instance, in your setLatestCaptureCut(). I'm assuming you already have some kind of routine that generates the CaptureCut and store it, to make it available in getLatestCaptureCut().

Replacing EventBus with RxJava - N subscribers always listening

I am replacing an EventBus pattern with RxJava in an Android app. I had events to alert any interested parties of updates to data in my cache singleton. Any time a web service was called, the data would be updated, and the subscribers would be alerted via a posted event.
I have something close to this set up in RxJava with AsyncSubject. The observers get a single event from the subject, but then they get an onComplete event and unsubscribe. This works as the UI first loads, but when the data needs to be refreshed, there are no subscribers to be notified. How do I tell those Subscribers to keep listening for more onNext events from the Subject?
I need a Subject that will report the most recent item. PublishSubject only emits items after subscription, so it doesn't quite meet my needs. My subscribers start observing at different times (possibly after the first data event), so I need the Subject to emit the last item observed and then keep the stream open for subsequent items. It seems like a combination of AsyncSubject and PublishSubject is what I need. Is there some way to accomplish this with the built in classes, or do I need to create my own subject?
WebServiceObservable OR CacheObservable
^
|
AsyncSubject
^
|
/ \
/ \
/ \
UiObserver1 UiObserver2
BehaviorSubject will fit your needs.
https://github.com/Netflix/RxJava/wiki/Subject#behaviorsubject
If you need more sophisticated behavior you can always write your own Subject implementation. It seems pretty straightforward to do so.
A little late answer but a slightly better option for your scenarion than the BehaviorSubject could be BehaviorRelay from RxRelay lib. and also for more global solutions when you need different behaviors but want to share single point of interaction between all modules you can use RxHub
I think it is more simple if you use BehaviorSubject with switchOnNext operator.
switchOnNext( )
convert an Observable that emits Observables (BehaviorSubject in this example) into a single Observable that emits the items emitted by the most-recently emitted of those Observables
the Observable returned by switchOnNext( ) unsubscribes from the previously-emitted Observable begins emitting items from the latest Observable
public class PhotoModel{
BehaviorSubject<Observable<Photo>> subject = BehaviorSubject.create(...);
public void setUserId(String id){
subject.onNext(Api.getUserPhoto(photoId));
}
public Observable<Photo> subscribeToPhoto(){
return Observable.switchOnNext(subject);
}
}
When should one use RxJava Observable and when simple Callback on Android?

Categories

Resources