RxAndroid Observable running on unexpected thread - android

I'm trying to create an Observable such that it will load some data from the network on an interval, and whenever the user refreshes the page. This is the gist of what I have so far:
PublishSubject<Long> refreshSubject = PublishSubject.create();
Observable<MyDataType> observable = Observable.merge(
Observable.interval(0, 3, TimeUnit.SECONDS),
refreshSubject
)
.flatMap(t -> {
// network operations that eventually return a value
// these operations are not observables themselves
// they are fully blocking network operations
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
// update ui with data
}, error -> {
// do something with error
});
Later in a refresh callback I have:
refreshSubject.onNext(0L);
It runs on the interval fine, however when I refresh, it explodes with a NetworkOnMainThreadException. I thought that I handled this with subscribeOn/observeOn. What am I missing? Also, why doesn't this cause a crash when the Observer is triggered from the interval?

You have to change your subscribeOn(Schedulers.io()) to observeOn(Schedulers.io()) and move it over your flatMap.
The reason for this is that your refreshSubject is a PublishSubject, which is an Observable and an Observer.
Since the onNext() of this PublishSubject is called inside the intern Observable first before the result gets delivered to your subscription.
This is also the reason that it works when you just use your Observable(and the fact that interval always subscribes to the computation thread by default).
Just check the output of those two snippets:
Observable.merge(
Observable.interval(0, 3, TimeUnit.SECONDS),
refreshSubject
)
.observeOn(Schedulers.io())
.doOnNext(aLong -> Log.d("Thread", Thread.currentThread().toString()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
Log.d("Subscribe Thread", Thread.currentThread().toString());
}, error -> {
// do something with error
});
vs
Observable.merge(
Observable.interval(0, 3, TimeUnit.SECONDS),
refreshSubject
)
.doOnNext(aLong -> Log.d("Thread", Thread.currentThread().toString()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
Log.d("Subscribe Thread", Thread.currentThread().toString());
}, error -> {
// do something with error
});

Related

rxjava timer throws timeout exception after subscribe has successfully executed

I have following code:
repo.getObservable()
.timeout(1, TimeUnit.MINUTES)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {
_isInProgress.value = true
}
.doFinally {
_isInProgress.value = false
}
.subscribe(
{
Timber.d("Success")
},
{
Timber.e(it)
})
.trackDisposable()
The problem with it is that I'm successfully getting Success message after couple seconds, yet my preloader still waits for 1 minute and then my error part of subscribe gets executed. Is that expected behaviour? What can I do to stop timeout if success part of subscribe gets executed?
P. S. Observable returned from getObservable() is created like this: PublishSubject.create()
If you need one result, use take(1) before or after timeout:
repo.getObservable()
.take(1) // <---------------------------------------------
.timeout(1, TimeUnit.MINUTES)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {
_isInProgress.value = true
}
.doFinally {
_isInProgress.value = false
}
.subscribe(
{
Timber.d("Success")
},
{
Timber.e(it)
})
.trackDisposable()
That must be because you are not calling onComplete on the PublishSubject and only onNext is called.
See this example:
PublishSubject<Integer> source = PublishSubject.create();
// It will get 1, 2, 3, 4 and onComplete
source.subscribe(getFirstObserver());
source.onNext(1);
source.onNext(2);
source.onNext(3);
// It will get 4 and onComplete for second observer also.
source.subscribe(getSecondObserver());
source.onNext(4);
source.onComplete();
Until onComplete is called the observers are waiting for more results.
You can either unsubscribe/dispose after the reception of the result you were waiting for or call onComplete on the Observable when all the results have been sent.

How to get a callback when every observer of a hot like ConnectableObservable complete?

I am searching for a doOn... callback for a ConnectableObservable that is invoked when every observer terminates
val gatewayItems = viewModel.getGatewayItems(gateways!!)
.observeOn(Schedulers.io())
.take(1)
.publish()
.autoConnect(2)
gatewayItems.subscribe { sharedGateways -> sharedGatewaysAdapter.submitList(sharedGateways) }
gatewayItems.subscribe { sharedGateways -> privateGatewaysAdapter.submitList(privateGateways) }
I would like to get a callback to my multicasted hot observable when both of my observers signal a terminal event
I have tried to put doOnTerminate and doOnComplete operators on my parent multicasted observable but, it seems that these callbacks are invoked 2 times (one for each observer)
val gatewayItems = viewModel.getGatewayItems(gateways!!)
.observeOn(Schedulers.io())
.take(1)
.doOnComplete { ... }
.doOnTerminate { ... }
.publish()
.autoConnect(2)
Both .doOnComplete and .doOnTerminate work for me.
Edit: the chances are you might be attaching the do... operators in incorrect order. For example, neither of these doOnComplete will work:
val gatewayItems = viewModel.getGatewayItems(gateways!!)
.observeOn(Schedulers.io())
.doOnComplete { ... }
.take(1)
.publish()
.autoConnect(2)
.doOnComplete { ... }

RxJava run part of the flatmap in main thread

Hi i am trying to implement a Single observable that chains two requests together.
In between the two requests i make, i notify a callback to update the UI with the response from request one and then launch the next request in the Schedulaers.io thread.
The issue i am having is that it tries to update the UI from the schedulars.io thread too and results to nothing being updated in the ui thread.
i cold wrap the calback on RunOnUiThread code block in android but wondering if there is a more elegant way of doing it?
i checked couroutines and it seems to just deal with putting a block of code in a seperate thread.
Here is my current code
override fun getHomeScreenInformation() {
delegator.requestOne()
.flatMap { responseOne->
homeScreenCallBack.onResponseOneRecieved(responseOne)
delegator.requestTwo()
}
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(
{responseTwo-> homeScreenCallBack.onResponseTwoRecieved(responseTwo)},
{error -> homeScreenCallBack.onError()}
)
}
Apply observeOn(AndroidSchedulers.mainThread()) as many times as necessary:
delegator.requestOne()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) // <----------------------
.flatMap { responseOne ->
homeScreenCallBack.onResponseOneRecieved(responseOne)
delegator.requestTwo()
.subscribeOn(Schedulers.io()) // <----------------------
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{responseTwo-> homeScreenCallBack.onResponseTwoRecieved(responseTwo)},
{error -> homeScreenCallBack.onError()}
)

RxAndroid Re-Subscribe to Observable onError and onComplete

My Question is probably more of the conceptual nature.
I get that by the Observable contract my Observable will not emit any more items after onComplete or onError is called.
But I'm using the RxBindings for Android and therefore it's not "my Observable" but the click on a Button that emits items.
fun observeForgotPasswordButton(): Disposable {
return view.observeForgotPasswordButton()
.flatMap {
authService.forgotPassword(email).toObservable<Any>()
}
.subscribe({
// on next
Timber.d("fun: onNext:")
}, { error ->
// on error
Timber.e(error, "fun: onError")
}, {
// onComplete
Timber.d("fun: onComplete")
})
}
observeForgotPasswordButton() returns an Observable
fun observeForgotPasswordButton(): Observable<Any> = RxView.clicks(b_forgot_password)
The problem is that authService.forgotPassword(email) is a Completable and it will call either onComplete or onError both of which lead to the fact that I cannot reuse the button anymore since the subscription ended.
Is there a way to circumvent this behavior?
Because in an error occurs I would like to be able to retry.
Also I would like it to be possible to send more then one password forgotten emails.
You can use the retry() and repeat() operators to automatically resubscribe to the original Observable (or Completable).
fun observeForgotPasswordButton(): Disposable {
return view.observeForgotPasswordButton()
.flatMap {
authService.forgotPassword(email).toObservable<Any>()
}
.repeat() // automatically resubscribe on completion
.retry() // automatically resubscribe on error
.subscribe({
// on next
Timber.d("fun: onNext:")
}, { error ->
// on error
Timber.e(error, "fun: onError")
}, {
// onComplete
Timber.d("fun: onComplete")
})
}

RxAndroid blocks UI thread

I have simple job which is selecting data using GreenDao. Also i want show ProgressWheel, but this job blocks UI thread.
showLoader();
DataManager.getInstance(this).getQuestions(0, 800)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(QuestionUtils::shuffleAnswers)
.subscribe(this::onFinishLoad);
And
public Observable<List<Question>> getQuestions(int offset, int limit) {
return Observable.fromCallable(() -> daoSession.getQuestionDao().queryBuilder()
.offset(offset)
.limit(limit)
.build().list());
}
It blocks because of the order of your operations.
DataManager.getInstance(this)
.getQuestions(0, 800)
.map(QuestionUtils::shuffleAnswers)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onFinishLoad);
should solve it. Else you would subscribe data and map them after.
If you want to finish the loader onec your data are emitted you can use action in your subscribe method and process the loading in your emitted nextData.
.subscribe (nextData -> {}, error -> {}, () -> doCompletedStuff())

Categories

Resources