Im new to rx and have some lines of code that confuse me:
Observable.just(1,2,3,4,5,6)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { value ->
Log.i("TEST", "$value")
}
.dispose()
it will not log the result but when i comment out subscribeOn () and observeOn() or dispose() then it works perfectly, like this:
Observable.just(1,2,3,4,5,6)
.subscribe { value ->
Log.i("TEST", "$value")
}
.dispose()
or
Observable.just(1,2,3,4,5,6)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { value ->
Log.i("TEST", "$value")
}
Can someone explain what happen inside this chain
When you write .subscribeOn(Schedulers.io()) this essentially means that Observable will operate on io thread, which will require a thread switch causing some delay.
by the time it happens you have already called the dispose() method which disposes the Observable hence you don't receive any output.
On the other hand if you remove
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
This means Observable will operate on the calling thread so no thread switch is required, hence you receive complete output before dispose() method call can be executed.
If you only remove dispose() then nothing is stopping the Observable from emitting its contents even though its executing on io
Observable.just(1,2,3,4,5,6) -> The Just operator converts an items into an Observable that emits these items.
subscribeOn() -> operator tells the source Observable which thread to emit and push items on all the way down to Observer
observeOn() -> it will switch and pass emissions using that Scheduler for the remaining (downstream) operations
subscribe() -> operator returns Disposable object. You should assign this object to variable or to CompositeDisposable object. All disposables should be dispose (using dispose() method) while your Activity or Fragment ends life to avoid memory leak.
More you can find here:
https://proandroiddev.com/understanding-rxjava-subscribeon-and-observeon-744b0c6a41ea
http://reactivex.io/documentation/operators.html#creating
You can also check Kotlin Coroutines as an alternative to RxJava
Related
I'm trying to retrieve items from a remote source, if this won't work (no internet) i'd like to retrieve cached items from a room database.
I have created a new single for when the error happens and I've specified on what thread it should subscribe and observe on. I still this exception though:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
This is the method for retrieving the items:
public LiveData<List<Article>> getNewsArticles() {
return LiveDataReactiveStreams.fromPublisher(
newsService.getNewsArticles()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.onErrorResumeNext(throwable ->
Single.just(newsDao.findAllForNumber(AMOUNT_OF_ARTICLES_PER_PAGE))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSuccess(newsArticles -> Completable.fromAction(() ->
newsDao.insertAll(newsArticles))
.subscribeOn(Schedulers.io()))
.toFlowable());
}
LiveDataReactiveStreams converts it into a livedata object and handles the subscribtion, so my view only knows about livedata.
I just can't seem to make the onErrorResumeNext call work on a background thread.
Any help would be greatly appreciated!
ANSWER
I ended up solving tthe problem with the following code:
public LiveData<List<Article>> getNewsArticles() {
return LiveDataReactiveStreams.fromPublisher(
newsService.getNewsArticles()
.observeOn(Schedulers.io())
.doOnSuccess(newsArticles -> newsDao.insertAll(newsArticles))
.onErrorResumeNext(throwable -> Single.fromCallable(() -> newsDao.findAllForNumber(AMOUNT_OF_ARTICLES_PER_PAGE)))
.toFlowable());
}
In RxJava method subscribeOn specify the Scheduler on which an Observable will operate. But method observeOn specify the Scheduler on which an observer will observe this Observable.
For simple:
Single
.zip(observable1.getList(), observable2.getAnotherList()) // Simple zip for example
.observeOn(AndroidSchedulers.mainThread()) // switch to main thread
.map(mapper.map(list1, list2)) // this command will execute on main thread
.observeOn(Schedulers.io()) // switch to io thread
.map(anotherMapper.map(complexList)) // this command will execute on io thread
.observeOn(AndroidSchedulers.mainThread()) // switch to main thread
.subscribeOn(Schedulers.io()) // specify thread for zip command
ObserveOn works only downstream. All the methods following the observeOn have been moved to the IO thread. While the methods prior to the observeOn are still in the main thread.
In your example you somewhere try to call room dao command on main thread, and system does not allow to execute on main thread. You can set breakpoint on each command then in IDE look exactly thread name where command will be execute.
For more complex examples see this article.
I am very new to RxAndroid and am still trying to navigate my way out of the errors that I am making.
Observable.just(RandomComputeManager.getChartData(0,"abcd",new Date()))
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s -> {
System.out.println("RXANDROID"+ s.getFood());
Toast.makeText(getActivity(),"HELLO"+s.getFood(),Toast.LENGTH_LONG);
});
I have a heavy computation method here that I am trying to run on RxJava's Schedulers.computation() thread.(I do not know if only calling it in Observable.just is the right way). The method is supposed to throw an exception if it does not have data.
Class RandomComputeManager{
public static getPieChartData(int a,String b,Date c) throws CustomException {
if(haveData){
//All Okay
}
else{
throw new CustomException("No Data");
}
}
The build is failing with error
error: unreported exception CustomException; must be caught or declared to be thrown
I have tried adding an observer to the subscribe method thinking that it has a onError method but neither is that solving this issue nor am I able to fetch my data then due to some ambiguity in the return value of the called method(Don't know if it should be an observable or just the object I need).
Please suggest a way to handle this.
The subscriber function can take another argument of throwable.
Please do like this
Observable.just(RandomComputeManager.getChartData(0,"abcd",new Date()))
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s -> {
System.out.println("RXANDROID"+ s.getFood());
Toast.makeText(getActivity(),"HELLO"+s.getFood(),Toast.LENGTH_LONG);
}, throwable ->{
});
Observable.fromCallable(RandomComputeManager.getChartData(0,"abcd",new Date()))
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s -> {
System.out.println("RXANDROID"+ s.getFood());
Toast.makeText(getActivity(),"HELLO"+s.getFood(),Toast.LENGTH_LONG);
}, Throwable :: printStackTrace);
This did the trick. Thanks to Gautam Kumar and Raj Suvariya for your help. I found this detail from Exception handling in rxjava
just method accepts parameters that are resolved immediately, so you actually are about to run the computation at the very same line you create your Observable. Also, this is the reason that your exception is not caught, as the getChartData is not called within Observable on-subscribe function.
What you need here is to create the Observable passing a computation function, but you are trying to pass the computed result.
I am used to Kotlin, so sorry if I mess up Java lambdas here, but you should use fromCallable here, like so
Observable.fromCallable(
() -> RandomComputeManager.getChartData(0, "abcd", new Date())))
fromCallable accepts a function that will execute once you subscribe and will emit the function result.
Also, for your purpose it's better to use a Single, as you will have only one item emitted.
Single.fromCallable(
() -> RandomComputeManager.getChartData(0, "abcd", new Date())))
Also if your your CustomException is checked and you don't want to crash, you do have to add onError handling, like already suggested by others.
I have a number of Observables that are used for network requests in my app. Since so much is the same, I apply an Observable transformation to them:
/**
* Creates a transformer that applies the schedulers and error handling for all of the observables in this ViewModel.
*/
private fun applyTransformations(): Observable.Transformer<NetworkState, NetworkState> {
return Observable.Transformer { observable ->
observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.onErrorReturn { NetworkState.Error(it) }
.doOnNext { publishState(it) }
.startWith(NetworkState.Loading())
}
}
The goals I am trying to accomplish with the above:
Apply consistent schedulers
Handle any error by returning an instance of my sealed class.
Handle any onNext by publishing the state returned by the observable.
Start off by sending a Loading state.
This works mostly fine, but what I've noticed is that while I call startWith and a loading state, it is never actually handled by doOnNext(). In other words, publishState() is never called for my loading state.
Where I set up the observables, I don't bother to add a subscriber, because the doOnNext() above is all that I'll need:
val subscription = repository.getInstagramPhotos(count)
.map { mapIGPhotoResponse(it) }
.compose(applyTransformations())
.subscribe()
If I were to supply a subscriber above, though, it would handle the loading state. It would also handle two onNext() calls - one for the subscriber supplied, and one for the doOnNext in the transform.
Is there a way to modify this startWith call to emit to whatever I've specified in doOnNext? I'm using RxJava 1.
Edit: Just to clarify some more, if I track what's emitted I expect to see two things. Loading -> Success. What I actually see is just Success. If I supply a subscriber to the observable I see Loading -> Success -> Success.
startWith should be before doOnNext.
Rxjava methods, though they look like they use the builder pattern, actually don't. They return a new observable each time an operator is applied. In your case, your doOnNext observable completes before your start with observable, so it's consumer isn't called with what you supply in startWith.
Ideally, you should go with:
observable
.startWith(NetworkState.Loading())
.doOnNext { publishState(it) }
.onErrorReturn { NetworkState.Error(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
Also, be careful with subscribing with no Consumer for onEror should it happen. Since you have nothing to consume the onError, RxJava will crash your app since it has nothing to notify for the error. Consider replacing the doOnNext with a Success Consumer in subscribe, and an empty Consumer for the error if you want to ignore it.
Also doOnNext is typically used for side effects, such as logging and the sort, they're more of a convenience than true functional operators.
I'm trying to replace my Handler method with RxJava.
My requirement:
I want to call the method getTransactionDetails() only after 5
seconds.
This my working code using Handler:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
getTransactionDetails();
}
}, 5000);
Rx java code - it's not working:
Observable.empty().delay(5000, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(o -> getTransactionDetails())
.subscribe();
This is how I would do it:
Completable.timer(5, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
.subscribe(this::getTransactionDetails);
A Completable represents a deferred computation with no value but an indication for completion or exception. The static method call timer() returns a Completable that signals completion after the specified time period has elapsed, and the subscribe() call will mean that the method getTransactionDetails() will be called on the current object when the timer fires. By supplying a Scheduler as the last argument to Completable.timer() you control which thread is used to execute getTransactionDetails().
doOnNext() is for side effects. It could be use for example for logging purpose, because logging doesn't alter the flow of data. But you want to pass down the result of getTransactionDetails(), so you should use map instead.
Second, Observable.empty() create an Observable that just propagate the finish/dispose message, it trigger neither doOnNext() neither map(). You could fix this using Observable.just() instead but a more idiomatic way would be to use Observable.timer():
Observable.timer(5000, TimeUnit.MILLISECONDS)
.map(o -> getTransactionDetails())
.observeOn(AndroidSchedulers.mainThread())
.subscribe( ... );
A final note on Schedulers. Observable.timer() and delay() are executed on the computation scheduler by default so you don't need to call .subscribeOn(Schedulers.io()) to execute getTransactionDetails() outside of main thread. Observable.timer() and delay() could take a Scheduler as a parameter if you want to control this. So you need to call .observeOn(AndroidSchedulers.mainThread()) if you want to use getTransactionDetails() result on UI thread. Every operator after observeOn() is executed on the defined Scheduler, so you have to put observeOn() after computation.
Edit: This is of course if you care about the result of getTransactionDetails(). If not, see Clyde answer.
Observable
.timer( 5000, TimeUnit.MILLISECONDS )
.subscribeOn(Schedulers.io())
.map(o -> getTransactionDetails() )
.subscribe();
you dont need observeOn because subscribe empty.
use timer it will be more correct way
It's not working, because delay() needs something to be emitted, but empty() actually emits nothing.
Instead, you should use Observable.timer().
Delay subscription already defined. Your example can be implemented in this way:
getTransactionDetails().delaySubscription(5000, TimeUnit.MILLISECONDS)
Try this
Observable.just(true).delay(5000, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(o -> getTransactionDetails())
.subscribe();
If you want a disposable that can be used with the view then use a Completable
Completable returns an Object which terminates as soon as one of the source Completables terminates (normally or with an error) and cancels all other Completables.
Eg :
Completable.timer(5, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
.subscribe(() -> {
// code after delay here
});
If you want for a service to get complete i would recommend a Subscription
Eg :
observable.timer(5000, TimeUnit.MILLISECONDS)
.map(o -> {
// your code after delay here
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe( ()-<{
// enter the service here
} );
I pretty much understand the concept of subscribe (any code below subscribeOn will be performed in that particular thread) and observe (same with subscribeOn) in rxandroid/rxjava.
What I want to happen is to perform long io operation in background thread then notify the main thread if the operations is finished. To do that, I'm thinking of having a flatmap which is subscribed in Schedulers.io() then observe a subscribe in AndroidSchedulers.mainThread(), something like this:
Observable.just(1)
.subscribeOn(Schedulers.io())
.flatMap(o -> {
longIO();
return null;})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(//i want to notify user here);
This is actually performing the longIO() in a different thread, thus not blocking the main thread, my problem is, this doesn't notify the main thread that longIO() is finished, note that android doesn't allow notifying user by creating Toast or AlertDialog if not in main thread. The code doesn't seem to pass through subscribe
Note: I used just(1) even though I don't use the integer 1 because I want the method inside flatMap to be performed. If I used empty it won't go through flatMap
The return type of flatMap is Observable. If the flatMap returns a null Observable, the subscriber won't get notified. Change the return statement to return Observable.just(null);
But, it's preferred to use Observable.fromCallable() to wrap your longIO() method, so just(1) would be obsolete and code looks cleaner. Note: the return type offromCallable() isn't Observable, so the subscriber would get notified even null is returned. It would look like:
Observable.fromCallable(() -> {
longIO;
return null;
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
I think that you are wrong in few things. IMO everything ABOVE subscribeOn() will be done in specific thread from thread pool. And of course everything BELOW observeOn should be pass into UI Thread.
Second thing - You cannot perform that your flatMap operator is returning null. You need to return Observable. If you don't need to pass data you can use : Observable.just(null) or Observable.never().
I think that better solution would be:
Observable.create(new Observable.OnSubscribe<Object>() {
#Override
public void call(final Subscriber<? super Object> subscriber) {
longIO();
}
})
.startWith(new Object()) //if you want to run it once
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();