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.
Related
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
I want to get MutableLiveData<List<Country>>() object from Observable<List<Country>>, but I'm not able to find a way.
I'm using implementation below, but it's not working. Is there any other way to achieve it, or am I doing something wrong with my current implementation?
dataManagerAnonymous.countries.toList().blockingGet().single()
The above code shows NetworkOnMainThreadException and crashes the Application.
dataManagerAnonymous
.countires
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { result ->
// result is List<Country>
}
dataManagerAnonymous.countires probably perform network request, which should be done on background thread in order to prevent UI thread from blocking. subscribeOn(Schedulers.io()) invokes your request on background thread. observeOn(AndroidSchedulers.mainThread()) lets you to use result of this call on UI thread. Subscribe block will be called when your request had been completed successfully and result data is ready to process.
I'm trying to create a reactive observable for Firebase Firestore calls.
I'm facing a threading issue. I'm using rxjava2 to handle threads and I don't want Firestore API to do that for me. It seems like Firestore calls are async, thus OnSuccess method is getting called on the main thread
Here is a simple example that showcases the issue:
Single<Integer> firestoreSingle = Single.create(emitter -> {
Log.d("TAG", Thread.currentThread().getName()); // -> RxCachedThreadScheduler-3 Thread
CollectionReference collectionRef = FirebaseFirestore.getInstance().collection("test_collection");
collectionRef.get().addOnSuccessListener(queryDocumentSnapshots -> {
Log.d("TAG",Thread.currentThread().getName()); // -> MAIN THREAD
List<DocumentSnapshot> documentSnapshotList = queryDocumentSnapshots.getDocuments();
emitter.onSuccess(documentSnapshotList.size());
}).addOnFailureListener(emitter::onError);
});
firestoreSingle
.subscribeOn(Schedulers.io())
.subscribe(howManyDocs -> {
Log.d("TAG",Thread.currentThread().getName()); // -> MAIN THREAD
Log.d("TAG","How many docs: " + howManyDocs);
});
Of course, I could add .observeOn(Schedulers.io()) to the reactive stream, but then I would not necessarily get the results on the same thread as the one I initially subscribed on.
I don't want the results neither in the main thread, nor in a different thread that the one I subscribed on.
Is there a way to query Firestore synchronously? How would you solve this issue?
You can use the answer that #MarkKeen suggested in the comment but for reference if you want to stick with RxJava, you can always call the method .blockingGet() to, as it suggests, block until a value is emitted.
trying to figure out some thing from this resource: https://www.raywenderlich.com/384-reactive-programming-with-rxandroid-in-kotlin-an-introduction
I am stuck with a question: why should I call subscribeOn() in the main thread instead of Schedulers.io()?
When I do subscription like that my app is freezing on a couple secongs and I am dropping frames.
searchTextObservable
.subscribeOn(Schedulers.io())
.map { cheeseSearchEngine.search(it) }
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
showResult(it)
}
And then I am subscribing in the main thread and observe it in Schedulers.io() (I am also don't understood why should I do it like that) app is not freezing at all.
searchTextObservable
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.map { cheeseSearchEngine.search(it) }
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
showResult(it)
}
Can anybody please explain why is it working like that ?
EDIT
// 1
private fun createTextChangeObservable(): Observable<String> {
// 2
val textChangeObservable = Observable.create<String> { emitter ->
// 3
val textWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable?) = Unit
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
// 4
override fun onTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
s?.toString()?.let { emitter.onNext(it) }
}
}
// 5
queryEditText.addTextChangedListener(textWatcher)
// 6
emitter.setCancellable {
queryEditText.removeTextChangedListener(textWatcher)
}
}
// 7
return textChangeObservable
}
subscribeOn vs. observeOn
subscribeOn will call create method of Observable on given Scheduler. It does not matter how many times you use subscribeOn. The first subscribeOn to source-observable (first in chain) always wins.
observeOn will switch thread from operator to operator. When upstream emits a value on Thread X it will be switched over to Thread Y from given Scheduler in observeOn-Operator. Everything below observeOn will be processed in Thread Y now.
Best guess on provided example 1:
Using subscribeOn will call Observable#create on Schedulers#io. Everything in the create-lambda will be called on this thread from Schedulers#io. The listener-callback (onTextChanged) can actually happen on another thread. In this case it is the UI-Thread, because it is some kind of UI element. Now onNext will be called from UI-Thread (emitter.onNext(it)). The value will be emitted to #map operator on UI-Thread (.map { cheeseSearchEngine.search(it) }) and cheeseSearchEngine#search will block the UI-Thread.
Example2:
Uses as first operator ".subscribeOn(AndroidSchedulers.mainThread())". This has actually no effect, because you are already in the UI-Thread. In this case the create-lambda will be called from AndroidSchedulers#mainThread. The onNext will be emitted on the UI-Thread as-well, just in Example1, because the UI triggers the onTextChanged-Event. The value will then be put through observeOn(Schedulers.io()). Everything from the observeOn-point will be executed on an Schedulers#io-Thread. This will in turn not block the ui, when map does some HTTP-request (or some sort of long running IO). After map is finished and emits the next value downstreams the next observeOn(AndroidSchedulers.mainThread()) will switch back to the UI-Thread. Therefore you can now saftly change the UI in the subscribe-lambda, because you are on the UI-Thread.
As a conclusion the first subscribeOn in Example2 can be omitted, if it does not matter from which Thread the listener-registration is happening (listener-reg must probably be thread-safe).
Summary:
Using subscribeOn will only invoke the create lambda on given Scheduler-Thread. The callback from registered listener in create may happen on another Thread.
This is why Example1 will block the UI-Thread and Example2 will not.
This is the beauty of Rx. Easy thread switching. Basically in Rx we can switch between different threads just by calling subscribeOn() or ObserveOn(). The difference between these two is, when subscribeOn(Thread1) is called, the task (in your example - cheeseSearchEngine.search(it)) runs on Thread1.
However, when you call observeOn(Thread2), the result of the task performed is give to Thread2. It means the result will be worked on Thread2. (In your example showResult will be called on Thread2)
So when you call subscribeOn(Schedulers.io()), the task is done on IO thread. Once the result is ready it will be given to Main UI thread on calling observeOn(AndroidSchedulers.mainThread()).
When done vice-versa, you are basically trying to do the task on UI thread rather using IO background thread. With this approach if you try to update any UI element, an exception will be thrown saying "UI elements can't be accessed from background thread (CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views)".
Hope I answer your question. Happy coding in Rx.
I think the code is a little misleading because of the .map operator, which is actually used to perform an expensive operation (search). A better way to do it would be to wrap the code with fromCallable and convert it to an async call with subscribeOn. Something like that:
searchTextObservable
// Means start a new async search every time text is changed
.flatMapSingle { Single
.fromCallable { cheeseSearchEngine.search(it) }
// This makes sure search is running on IO thread
// This way expensive operation is done off the main thread, which eliminates the freeze
.subscribeOn(Schedulers.io()) }
// This makes sure that results will be handled on main thread
// Important because you can only access Android Widgets from the main thread
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
showResult(it)
}
The explanationas are in Code. I think the intent is much clearer now. For details see the answer by HansWursrt
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();