For example we have a call:
getObsevable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(::onSuccess, ::onError))
Is there is a way to not launch it if condition is true? For example:
getObsevable()
.stop({ Calendar.getInstance().get(Calendar.MINUTE) % 2 == 0 })
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(::onSuccess, ::onError))
In this code I mean we should not launch observable if current minute is odd. And launch it if current minute is even.
Yes, you can use filter to achieve this. So what happens when filter is used, if the condition doesn't meet, the stream below doesn't get executed.
In your case (or example), if the current minute is odd, the subscribe method wont be called. However, if it's even, the stream will work just the way it's written.
getObservable()
.filter({ Calendar.getInstance().get(Calendar.MINUTE) % 2 == 0 })
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(::onSuccess, ::onError))
You can read about it here -> http://reactivex.io/documentation/operators/filter.html
Assign it to a disposable, and dispose it before start another call, or wherever you need to dispose it
Disposable disposable;
public void createDisposableWithMyObservable(){
if(disposable != null) disposable.dispose();
disposable = getObsevable()
.stop({ Calendar.getInstance().get(Calendar.MINUTE) % 2 == 0 })
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(::onSuccess, ::onError));
}
Related
Are there any clear and smart solution to dispose observable from its subscribe method?
E.g. I have an Observable.interval that emits Long every second. In my subscribe method i want to check if 20 seconds gone than dismiss subscription.
val observable = Observable.interval(1000,TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ num ->
if(num == 20.toLong())
{
observable.dispose()//I cant call dispose here because variable 'observable' is not avaliable yet
}
})
What is the easiest and right way to achieve this logic?
I found one simple solution. Just divide variable declaration and initialization in to two steps.
E.g.
var observable:Disposable? = null
observable = Observable.interval(1000,TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ num ->
if(num == 20.toLong())
{
observable?.dispose()
}
})
I am trying to chain two network calls in my Android app. I am using Retrofit. Basically I want to do :
Make API Call to login
Wait for the response of login, save the token to SharedPrefs
Make another API call right after I've saved the token
Wait for the response, save the data
I think I have chained the stream in the right way, the only thing is I want to update the UI in between. For example once the call starts I want to display a progressDialog ( I do that in doOnSubscribe ), or dismiss the Dialog once the call has completed ( I do that in doOnComplete ). However I get the exception Only the original thread that created a view hierarchy can touch its views. I subscribe on the io thread and observe on the mainThread so that I can make the changes to the UI, however I must be missing something.
I tried adding .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
lower in the stream, but I still get the same error message.
getView().onLoginAction().subscribe(aVoid -> Observable.combineLatest(
getView().userNameObservable().map(CharSequence::toString),
getView().passwordObservable().map(CharSequence::toString),
Pair::new)
.first()
.subscribe(usernamePasswordPair -> {
User user = User.create(usernamePasswordPair.first, usernamePasswordPair.second, "");
RetrofitClientInstance.createService(AuthenticationNetworkApi.class).login(new Login(user.username(), user.password()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(loginResponse -> {
AuthorizationResponse responseBody = loginResponse.body();
if (responseBody != null && responseBody.getAccessToken() != null && !responseBody.getAccessToken().isEmpty()) {
if (localStorage.getAccessToken().isEmpty()) {
localStorage.saveAccessToken(responseBody.getAccessToken());
}
}
}
).
doOnSubscribe( action -> getView().showProgressDialog())
.doOnError(error -> {
getView().dismissProgressDialog();
getView().showErrorMessage("Login Unsuccessful");
}).doOnComplete(() -> getView().dismissProgressDialog()
)
.flatMap(response -> RetrofitClientInstance.createService(ActivitiesApi.class).getUserActivities())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(activities -> {
for (UserActivityApiModel useractivity : activities
) {
activityService.addActivity(Activity.create(Integer.parseInt(useractivity.getId()), useractivity.getActivityName(), useractivity.getDate(),
Integer.parseInt(useractivity.getValue()), Integer.parseInt(useractivity.getSubCategory().getId())));
}
}).doOnError(error -> getView().showErrorMessage(error.getMessage()))
.doOnComplete(() -> getView().redirectToHomeScreen())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
}));
The error occurs here :
.doOnError(error -> {
getView().dismissProgressDialog();
getView().showErrorMessage("Login Unsuccessful");
})
It seems you are using a different thread to execute your backend. In that case, you can't touch the main UI thread from the second one. You need to execute first runOnUiThread { //your code }
In //your code, call the two lines of code that you put on doOnError.
I'm using rxJava and I want to do a forEach of a list, and for every item, make a call, and then once those calls are finished, call another one.
This is my code
val flowableList = answerListCreated.map {
questionService.addAnswerToQuestion(
questionId,
it.id,
MyUtils.getAccessTokenFromLocalStorage(context = mContext!!)
)
}
disposable = Flowable.concat(flowableList)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
addCorrectsAnswersToQuestion(questionId)
}
But it's joining in the subscribe twice, and it should join in the subscribe once.
What I'm missing? I thought concat should be a good option because I've read that it does first the first job, and then when job1 is finished it starts the job2.
Well, also if necessary I can return Observable<T>, from now in my service I'm returning Flowable<T> to test this.
i think you need to do something like:
val disposable = Flowable.fromArray(answerListCreated)
.flatMap {
questionService.addAnswerToQuestion(
questionId,
it.id,
MyUtils.getAccessTokenFromLocalStorage(context = mContext!!)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
.toList()
.subscribe {
addCorrectsAnswersToQuestion(questionId)
}
I have a State(Enum) that contains (Good, Non-Critical, Critical) values
So requirement is :
should trigger when state goes in non-critical state.
should trigger when state goes in critical state.
should trigger when state stays in critical state for 15 seconds.
Input :
publishSubject.onNext("Good")
publishSubject.onNext("Critcal")
publishSubject.onNext("Critcal")
publishSubject.onNext("NonCritical")
publishSubject.onNext("Critacal")
publishSubject.onNext("Critical")
publishSubject.onNext("Good")
and so on...
See Code Structure for Reference:
var publishSubject = PublishSubject.create<State>()
publishSubject.onNext(stateObject)
publishSubject
/* Business Logic Required Here ?? */
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
AppLogger.printLog("Trigger Success --> ")
}
Please help,
Thanks in Advance,
You can use distinctUntilChanged() to suppress events that don't change the state. Filter out the normal events using filter().
Use the switchMap() operator to create a new subscription when the state changes. When the state is "critical", use the interval() operator to wait out the 15 seconds. If the state changes in that 15 seconds, switchMap() will unsubscribe and re-subscribe to a new observable.
publishSubject
.distinctUntilChanged()
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.filter( state -> state != State.Normal )
.switchMap( state -> {
if (state == State.Critical) {
return Observable.interval(0, 15, TimeUnit.SECONDS) // Note 1
.map(v -> State.Critical); // Note 2
}
return Observable.just( State.Noncritical );
})
.subscribe( ... );
interval() is given an initial value of 0, causing it to emit a value immediately. After 15 seconds, the next value will be emitted, and so on.
The map() operator turns the Long emitted by interval() into
The first two parts of your requirements should be combined into one. You're asking for the chain to be triggered on NonCritical and Critical events, ergo the chain should not be triggered for Good event. Likewise, you only need to trigger an event if the state is different from a previous event. For this two .filter events should suffice:
var lastKnownState: State = null
publishSubject
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.filter(this::checkStateDiffers) // Check we have a new state
.filter { state -> state != State.Good } // Check event is good
.subscribe {
AppLogger.printLog("Trigger Success --> ")
}
...
private fun checkStateDiffers(val state: State): Boolean {
val isDifferent = state != lastKnownState
if (isDifferent) lastKnownState = state // Update known state if changed
return isDifferent
}
The timeout requirement is a bit trickier. RxJava's timeout() operator gives the option of emitting an error when nothing new has been received for a period of time. However I am assuming that you want to keep listening for events even after you receive a timeout. Likewise, if we just send another Critical event it'll be dropped by the first filter. So in this case I'd recommend a second disposable that just has the job of listening for this timeout.
Disposable timeoutDisp = publishSubject
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.timeout(15, TimeUnit.SECONDS)
.onErrorResumeNext(State.Timeout)
.filter { state -> state == State.Timeout }
.filter { state -> lastKnownState == State.Critical }
.subscribe {
AppLogger.printLog("Timeout Success --> ")
}
Also adjust the checkStateDiffers() to not save this Timeout state in the first chain.
private fun checkStateDiffers(val state: State): Boolean {
if (state == State.Timeout) return true
var isDifferent = state != lastKnownState
if (isDifferent) lastKnownState = state // Update known state if changed
return isDifferent
}
Here's a simplified version of what I'm trying to do (using Kotlin and RxJava)
makeServerCall()
.doOnNext {
doStuff(it)
}
//TODO: if it == 0, call asyncOperation() and wait for its callback to fire
//before running the rest of the stream. Otherwise immediately run the rest
//of the stream
.flatMap {
observable1(it)
observable2(it)
Observable.merge(
getSpotSearchObservable(observable1),
getSpotSearchObservable(observable2)
}
.subscribeBy(onNext = {
allDone()
view?
})
How do I squeeze in the call to asyncOperation() and make the rest of the stream wait for its callback to fire, but only when a certain condition is met? This seems like it's probably a trivial operation in Rx, but no obvious solution is coming to mind.
FlatMap it!
.flatMap {
if (it == 0) {
return#flatMap asyncOperation()
.ignoreElements()
.andThen(Observable.just(0))
}
return#flatMap Observable.just(it)
}
.flatMap {
observable1(it)
observable2(it)
Observable.merge(
getSpotSearchObservable(observable1),
getSpotSearchObservable(observable2)
)
}