I'm having an issue trying to understand, in a reactive way, how simultaneous operations to the same observable should work.
The scenario is the following:
I have a list of users and a remove button.
Every time I press remove I'm making a call to the API: UsersApi.removeUser. It is possible to remove multiple users at the same time. Which means that multiple UsersApi.removeUser are happening simultaneously.
After each UsersApi.removeUser I need to make a UsersApi.refreshUser call
So in terms of pseudo code what I am doing when clicking remove is the following:
Presenter:
public Observable<User> removeUser(int userId) {
return UsersApi.removeUser(userId)
.flatMap(user -> UsersApi.refreshUser(userId));
}
Fragment:
public void removeUser() {
presenter.removeUser(userId)
.subscribe(user -> {
//remove user from ui
// update number of total users
})
}
The problem with this approach is that because of the asynchronous nature of the remove (multiple removes allowed) I cannot guarantee that what is reaching the subscribe is the latest one. The subscribe will be reached twice, one for each remove, and the user info might not be updated or the latest. Does that make sense?
What I want to happen:
Parallel/Simultaneous remove calls using a reactive approach (triggered by multiple remove clicks from the user)
After a remove call finishes, start the next remove call
Edit: What I would like to know is how to do/if is possible to do the solution I did (see edit2) using Rx operators.
Edit2: My solution for this was to enqueue the user operations (in this case remove) and emit, using a PublishSubject, when the UsersApi.refreshUser(userId) call finishes.
So basically what I did was (pseudo code):
private final PublishSubject<UserOperation> userOperationObs;
private final ConcurrentLinkedQueue<UserOperation> pendingOperations;
private boolean executingOperation;
private void emitUserOperation(final UserOperation operation) {
if (!executingOperation) {
executingOperation = true;
userOperationObs.onNext(operation);
} else {
executingOperation.add(operation);
}
}
public Observable<User> removeUser(UserOperation operation) {
return UsersApi.removeUser(operation.getUserId)
.switchMap(user -> UsersApi.refreshUser(operation.getUserId))
.doOnNext(user -> {
executingOperation = false;
final UserOperation nextOperation = pendingOperations.poll();
if (nextOperation != null) {
userOperationObs.onNext(operation);
}
};
}
You could turn your UI click into Observable (eg. by using RxBinding). After that, you could use concatMap operator to perform api call so it will start next network call once current api call is finished.
// emit clicks as stream
Observable<?> clicks = RxView.clicks(removeView)
// listen clicks then perform network call in sequence
clicks.concatMap(ignored -> usersApi.refreshUser(userId))
Related
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (event.action == KeyEvent.ACTION_DOWN) {
val status = operation() // operation takes time
return status
}
return super.onKeyDown(keyCode, event)
}
When an event occurs, the above handler gets called. Now if it requires time to decide whether to pass the true or false status to next layers (super) inside if block, how can the flow be designed properly. I am required to get the result asynchronously because the time to decide return value (i.e. true or false) might be longer and the function shouldn't keep main thread await. So, I need to find other way to make the super call delayed.
What is the proper way to get rid of this problem? Any specific design pattern to deal this kind of problem?
Please ignore the language.
Update 1
I have been thinking to store keyCode and event and return true (means the event was consumed and no need to re consume by other observer) immediately, and then after operation() is completed i have the status available and now I can re trigger the pending stored event with same keyCode and event. But not all event provides functions to trigger manually. How can I do so for events that can not be triggered manually.
My proposed solution
private fun doOperation(callback: (status:Boolean) -> Unit) {
Handler().postDelayed({
callback.invoke( arrayOf(true, false).random() )
}, 5000)
}
var pendingEvent: KeyEvent? = null
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (event.action == KeyEvent.ACTION_DOWN) {
doOperation {
if (it && pendingEvent != null){
dispatchKeyEvent(pendingEvent)
pendingEvent = null
} else {
// do nothing
}
}
return true // let other know I consumed it
}
return super.onKeyDown(keyCode, event)
}
Is this a proper way? What can be bad of this thought?
Because events are fire-and-forget (no return value or void) it should be clear that asynchronous events or event handlers that return a value are a contradiction in itself or a paradox. When asynchronous means "waiting without blocking" and event "notify without waiting" you are obviously creating more problems than solutions. A return value also implies that the caller is waiting for completion of the operation and is interested in the result.
I recommend to restructure your application.
An event handler should never return a value or be asynchronous.
what if the observer needs more time to decide if it consumes or not?
How do you deal it?
This decision (or decisions in general) depends always on the state of at least one variable.
There are two situations
the state is known at the moment the observer gets notified or
the state is unknown at this moment (the observer needs more time).
Situation 1) requires no waiting, but situation 2) does.
In case of situation 2), the change of the state is always triggered by an operation. The execution duration of this operation determines how long the waiting time is. This operation must raise an event, when the relevant state has changed.
In general you have three options to wait:
Keep spinning until a condition is met like polling (e.g. infinite
loop): while(true){}.
Use a timer and after the elapsed time do the action
Use events whenever you need to wait.
The first two options will block a thread. If the thread is the same as the observable thread, than you are blocking the observable and all other waiting observers too. If that thread is an UI thread, then the UI will stall and become unresponsive. Events are a pattern that will solve the blocking issue.
Let's imagine the following scenario: you want to start a specific animation. You have two constraints: the type of the animation depends on which a key was pressed AND before you can start a new animation you have to wait until the first one completes. E.g. when TAB was pressed move a rectangle from left to right. When ENTER was pressed move a rectangle from top to bottom.
This introduces two waiting situations: key pressed and animation completed. To handle the waiting, you would create and associate an event for each potential waiting situation: keyPressed and animationStopped event:
The keyboard key pressed event
The interface to be implemented by the observer that is waiting for a particular key to be pressed:
interface IKeyPressedListener {
void onKeyPressed(int keyCode);
}
The event interface to be implemented by the observable that exposes and raises the event:
interface IKeyPressedEvent {
void subscribeToKeyPressedEvent(IKeyPressedListener listener);
void unsubscribeToKeyPressedEvent(IKeyPressedListener listener);
}
The animation event
The interface to be implemented by the observer that is waiting for an animation to stop:
interface IAnimationStoppedListener {
void onAnimationStopped();
}
The event interface to be implemented by the observable that exposes and raises the event:
interface IAnimationStoppedEvent {
void subscribeToAnimationStoppedEvent(IAnimationStoppedListener listener);
void unsubscribeToAnimationStoppedEvent(IAnimationStoppedListener listener);
}
The actual event listener
The implementation of the class that plays an animation on key pressed:
class AnimationController implements IKeyPressedListener, IAnimationStoppedListener
{
// store the key that was pressed,
// so that an event that will be raised at a later can process it
private int keyCodeOfLastKeyPressed = 0;
// The reference to the class that exposes
// the keyPressedEvent by implementing IKeyPressedEvent
KeyboardController keyboardController;
// The reference to the class that exposes
// the animationStoppedEvent by implementing IAnimationStoppedEvent
AnimationPlayer animationPlayer;
// Constructor
public AnimationController() {
this.keyboardController = new KeyboardController();
this.animationPlayer = new AnimationPlayer();
// Subscribe to the key pressed event
this.keyboardController.subscribeToKeyPressedEvent(this);
}
#Override
public void onKeyPressed(int keyCode) {
if (this.animationPlayer.hasPlayingAnimation) {
// Instead of waiting that the animation completes
// subscribe to an event and store the relevant data
this.keyCodeOfLastKeyPressed = keyCode;
this.animationPlayer.subscribeToAnimationStoppedEvent(this);
}
else {
// There is no playing animation, so no need to wait
this.animationPlayer.playAnimation(keyCode);
}
}
// After a while this handler will be invoked by the event source.
#Override
public void onAnimationStopped() {
// To avoid memory leaks unsubscribe first
this.animationPlayer.unsubscribeToAnimationStoppedEvent(this);
// Since we stored the key code earlier, we can continue to process it
// and start a new animation that maps to a specific key
this.animationPlayer.playAnimation(this.keyCodeOfLastKeyPressed);
}
}
Following the Observer Pattern avoids thread blocking waiting time. The application can just leave the context and return when the event occurred (in this case the AnimationStopped event). To store the change value (event args) of an event, a private shared field is introduced, so that the second event handler can access and finally process it.
Using Observer-pattern may help you.
You can use Debounce operator (debounce(DEBOUNCE_TIMEOUT.toLong(), TimeUnit.MILLISECONDS)) to delay the event.
only emit an item from an Observable if a particular timespan has passed without it emitting another item
Check the official documentation for how to use
Edit 1
Code snippet
RxView.clicks(mButton)
.debounce(300, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.subscribe(...)
So I am trying to build a dialog fragment screen (Android) which has an EditText where users can input their desired username. Using the RxBindings library's RxTextView.textChanges() I am observing the EditText and doing the following:
Resetting existing hints and disabling the "Confirm" button.
Filtering out invalid strings using a regex pattern.
Making a request to the server to check if the valid string is available.
Showing the appropriate response and, if applicable, enabling the "Confirm" button.
My repository's checkUsername() method returns a Single<Boolean> to denote whether the username was available or not. Here's the code:
Observable<String> usernameObservable = RxTextView.textChanges(usernameEditText)
.doOnNext(charSequence -> resetUsernameChecks())
.filter(charSequence -> !TextUtils.isEmpty(charSequence))
.debounce(400, TimeUnit.MILLISECONDS)
.map(CharSequence::toString)
.filter(s -> {
boolean match = s.matches("^(?=.{4,12}$)(?![_.])(?!.*[_.]{2})[a-zA-Z0-9._]+(?<![_.])$");
if (!match) showMessage(R.string.profile_dialog_username_hint_invalid, true);
return match;
});
Disposable usernameSubscription = usernameObservable
.subscribe(username -> {
Disposable d = viewModel.checkUsername(username)
.subscribe(available -> {
if (available) {
claimButton.setEnabled(true);
showMessage(R.string.profile_dialog_username_hint_available, false);
} else {
showMessage(R.string.profile_dialog_username_hint_unavailable, true);
}
});
disposables.add(d);
});
disposables.add(usernameSubscription);
With the following helper methods:
#UiThread
private void resetUsernameChecks() {
claimButton.setEnabled(false);
usernameInputLayout.setError(null);
}
#UiThread
private void showMessage(#StringRes int message, boolean error) {
showMessage(getString(message), error);
}
#UiThread
private void showMessage(String message, boolean error) {
if (error) {
usernameInputLayout.setErrorTextAppearance(R.style.AppTheme_RedMessage);
} else {
usernameInputLayout.setErrorTextAppearance(R.style.AppTheme_GreenMessage);
}
usernameInputLayout.setError(message);
}
However, the issue I run into is that either of the showMessage() methods cause a crash because the there may either be a background thread in the running (thus preventing changes to the UI) or that a previously called checkUsername() lingers back and messes everything up.
Essentially, how can I make sure I do work on the correct thread and cancel any lingering network calls before making a new one?
EDIT:
I fixed the thread issue by encapsulating the UI methods inside a Runnable. However, I still have issue with cancelling previous call. Lets say the user searches for an available username -- nick -- but then quickly hits backspace. The already fired network call now comes back and inaccurately shows that nic is available even though it may not be.
So, for you to update UI, you gotta do it in UI Thread. I guess,
usernameInputLayout.setErrorTextAppearance(R.style.AppTheme_GreenMessage);
is like the UI thread thing, so you need to use subscribeOn.
If you would show more from the log, we would know the error. But, right now, your best option is to use UI Thread to update UI.
usernameObservable.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(v -> //whatever you want )
Edit: My bad about "Observable by default runs on background thread." It is wrong. The correct is: It runs in the thread it is defined in, unless we use subscribeOn()/observeOn() methods.
I have an async method makeRequest() with callback. It called many times from different classes of my application. I need that this calls start one by one and never simultaneously.
I want to implement this using Rx. Like this:
public void execute() { // This method called many times from another classes
Observable.just(true)
// what I need to add here?
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(o -> {
internalExecute();
return o;
})
.subscribe();
}
private void internalExecute() { // This method should called only when previous call was finished
makeRequest(this::onRequestFinished);
}
private void onRequestFinished() {
// here is I handle request finish
}
But at now all requests works at parallel. What I need to add here to run requests one by one?
According to comments, you have here separated streams and requests. each client that execute request expect a result from the request. but no requests allowed to run in parallel, in this case I think the easiest way is to limit the Scheduler to an application global background sequential thread Executor, i.e:
Schedulers.from(Executors.newSingleThreadExecutor())
provide somewhere in your app this single thread Executor, in singleton manner of course, it's important that each request stream will use the same object:
private final Scheduler singleThreadScheduler = Schedulers.from(Executors.newSingleThreadExecutor());
public void execute() { // This method called many times from another classes
Observable.just(true)
.map(o -> {
internalExecute();
return o;
})
.subscribeOn(singleThreadScheduler)
.subscribe();
}
private void internalExecute() { // This method should called only when previous call was finished
makeRequest(this::onRequestFinished);
}
private void onRequestFinished() {
//NOTE: you should make sure that the callback execute where you need it (main thread?)
// here is I handle request finish
}
besides that, you're not exposing Observable outside, to the clients, but rather using callback mechanism, you can leverage reactive approach further, by making execute() returning Observable. (and enjoy composition of Obesrvables, operators, proper use of observeOn/subscribeOn, error handling with onError, disposing/unsubscribing etc.), as you're using async api, you can use fromEmitter()/create() (in newer RxJava1 version)), read more here:
private final Scheduler singleThreadScheduler = Schedulers.from(Executors.newSingleThreadExecutor());
public Observable<Result> execute() { // This method called many times from another classes
return Observable.fromEmitter(new Action1<Emitter<? extends Object>>() {
#Override
public void call(Emitter<?> emitter) {
emitter.setCancellation(() -> {
//cancel request on unsubscribing
});
makeRequest(result -> {
emitter.onNext(result);
});
}
})
.subscribeOn(singleThreadScheduler)
}
I'm learning RxJava so please be gentle. I've watched the tutorials, done the reading, searched SO, however, I'm still having some problems transforming my AsyncTaskLoader. For some reason, I can't find a pattern of operators to achieve my task (although I think it's a common one). What I'm trying to do is the following: return an Observable my fragment could subscribe to. The observable should do the following on subscribe:
1) Fetch data from the local database by doing 2 queries, running some logic and returning results;
2) Fetching data from API;
3) Synchronising the new API data with the database;
4) Repeating step one and returning results;
So far I've transformed my db calls and my API calls to return observables. I'm trying to understand how I can emit the cold results and continue with the chain. I could probably keep the two operations separately, and use the same subscriber to subscribe to both? But I'm not sure how that would work if my new loader-replacement class returns an observable... Also I don't really need to process the results from the second observable - I just need for the first one to replay when the second one finished.
So far I have the following:
public Observable<StuffFetchResult> getColdStuff() {
return Observable.zip(mDataSource.listStuff(), mDataSource.listOtherStuff(),
(stuff, moreStuff) -> {
List<Stuff> mergedList = new ArrayList<>();
// do some merging stuff
return new StuffFetchResult(mergedList);
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
Assume I also have getHotStuff() that will do the API call and the synchronisation with the database, if that's the right approach, and return the same Observable. However, I'm stuck on the next step - how can I restart the first observable to replay once hotStuff has completed, without adding another subscriber?
EDIT:
I've made some progress and I think all I need now is to join it all up. I have my two methods:
1) getColdStuff() is pretty much as described above
2) getHotStuff() will do call to the API, synchronise with the database, and return an Observable. The idea was to call getColdStuff() again after getHotStuff() has finished in order to refresh the UI, so actual result returned from getHotStuff() can be ignored. All it needs to do is to trigger getColdStuff() once done.
I've tried the suggestion in the answer to and created the following:
BehaviorRelay<Observable<StuffFetchResult>> callSequence = BehaviorRelay.create();
Observable<StuffFetchResult> valueSequence = Observable.switchOnNextDelayError(callSequence.toSerialized());
valueSequence.subscribe(new Subscriber<StuffFetchResult>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(StuffFetchResult result) {
// UI stuff
}
});
callSequence.call(loader.getColdStuff());
I can subscribe to valueSequence here and use callSequence.call(loader.getColdStuff());, which will run the first method and produce results in onNext() of my subscription, which I can use for my UI. However, I'm not sure how to run getHotStuff() in parallel and also do a different action on it when it returns. Also getHotStuff() returns a different type of Observable so I can't really use the same callSequence?
EDIT 2
Using two subscribers, I can achieve the required behaviour I think. Not really sure if that's the right way to go about it though.
loader.getHotStuff()
.subscribeOn(Schedulers.io())
.subscribe( new Subscriber<Object>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(Object stuffWeDontCareAbout) {
callSequence.call(loader.getColdStuff());
}
});
if i understand your scenario correctly, you may want something like that -
BehaviorSubject<Observable<T> callSequence = BehaviorSubject.create();
Observable<T> valueSequence = Observable.swithOnNextDelayError(callSequence.toSerialized());
your subscriber will be listening to the valueSequence, and whenever you need to "restart", you will call this -
callSequence.onNext(call.cache()); // *call* is Observable<T>
(i leave the .subscribeOn/.observeOn configuration to you)
I am looking for a way, hopefully using RxJava for consistency, to monitor the progress of multiple subscribers that may be fired at different times. I am aware of how to merge or flatMap subscribers together when they are all fired from one method but I am unaware of a way to do it when they are fired at different times from different methods.
For example, if I have 2 long running tasks attached to button presses. I push button 1 and fire off the observable/subscriber, half way through running I push button 2 to fire off the second observable/subscriber.
I want to enable a button when no tasks are running and disable it when one or more tasks are running.
Is this possible? I am trying to avoid setting instance variable flags as well.
I would use a separate BehaviorSubject and scan to monitor execution status. This is quite similar to an instance variable, but probably it can inspire you to a better solution. Something like this:
private final BehaviorSubject<Integer> mProgressSubject = BehaviorSubject.create(0);
public Observable<String> firstLongRunningOperations() {
return Observable.just("First")
.doOnSubscribe(() -> mProgressSubject.onNext(1))
.finallyDo(() -> mProgressSubject.onNext(-1)));
}
public Observable<String> secondLongRunningOperations() {
return Observable.just("Second")
.doOnSubscribe(() -> mProgressSubject.onNext(1))
.finallyDo(() -> mProgressSubject.onNext(-1));
}
public Observable<Boolean> isOperationInProgress() {
return mProgressSubject.asObservable()
.scan((sum, item) -> sum + item)
.map(sum -> sum > 0);
}
Usage will be like this:
isOperationInProgress()
.subscribe(inProgress -> {
if (inProgress) {
//disable controls
} else {
//enable controls
}
});
With this approach you can have any number of long running operation and you do not have to fire them all. Just don't forget to call doOnSubscribe and finallyDo.
PS. Sorry, I didn't test it, but it should work.
To make this possible, let both long running operations emit an onNext event on a PublishSubject. Combine both Subjects with a zip or combineLatest function and subscribe to this. Once the combine function receives an event, this means that both Subjects have emitted an onNext event, thus both long running operations have finished and you can enable the 3rd button.
private PublishSubject<Boolean> firstSubject = PublishSubject.create();
private PublishSubject<Boolean> secondSubject = PublishSubject.create();
#Override
public void onStart() {
super.onStart();
subscribeToResult();
}
private Observable<Integer> firstOperation() {
return Observable.just(100)
.delay(1000) // takes a while
.subscribe(tick -> firstSubject.onNext(true));
}
private Observable<Integer> firstOperation() {
return Observable.just(200)
.delay(1000) // takes a while
.subscribe(tick -> secondSubject.onNext(true));
}
private void subscribeToResult() {
Observable.zip(
firstSubject,
secondSubject,
(firstResult, secondResult) -> return true
).subscribe(
tick -> thirdButton.setEnabled(true)
)
}
Definitely take a look at the RxJava combine functions.