Why should I subscribe on the main thread? - android

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

Related

Could it be that Flow.collect() blocks the UI?

I'm reading the article about flows on kotlinlang.org: https://kotlinlang.org/docs/flow.html
They show the next example:
fun simple(): Flow<Int> = flow {
println("Flow started")
for (i in 1..3) {
delay(100)
emit(I)
}
}
fun main() = runBlocking<Unit> {
println("Calling simple function...")
val flow = simple()
println("Calling collect...")
flow.collect { value -> println(value) }
println("Calling collect again...")
flow.collect { value -> println(value) }
}
And they say that the output is:
Calling simple function...
Calling collect...
Flow started
1
2
3
Calling collect again...
Flow started
1
2
3
Therefore, it seems like the UI thread is waiting for the first flow.collect function to finish, before continuing to print "Calling collect again..."
I would expect that while the first flow builder runs, the system will print "Calling collect again...", so the output would be:
Calling simple function...
Calling collect...
Calling collect again...
Flow started
1
2
3
Flow started
1
2
3
What am I missing?
collect is a suspend function. Suspend functions are synchronous in the calling code, so collect is no different than calling forEach on a List, as far as code execution order is concerned.
It's not blocking the calling thread, but it is suspending, which means the code in the coroutine waits for it to return before continuing. This is the primary feature of coroutines--that you can call time-consuming code synchronously without blocking the thread. Under the hood, the suspend function is doing something asynchronously, but to the coroutine that calls it, it is treated as synchronous.
Under the hood, the coroutine dispatcher is breaking up your coroutine into chunks that it passes to the calling thread to run. In between these chunks, where it is suspending, the calling thread is free to do other stuff, so it's not blocked.
My answer here might help with explaining the concept further.

Where to put reused values used for every emission in Observable/ Flowable?

I have some expensive operations that only need to be performed once (e.g. load/ download large files, load large ML models, or calculate optimized data structure based on some other data). I want to use this for every value the Observable/ Flowable generates:
The following code works, but it runs heavyProcessing() and heavyProcessing2() on the caller's thread. In my case, I can't choose what my callers thread (its the main thread because I am using WorkManager's RxWorker, which calls createWork from main). Therefore, start blocks the main thread. How do I get heavyProcessing to be performed in the background with RxJava and also available to the subsequent RxJava chain?
fun start(): Observable<Unit> {
val heavy = heavyProcessing() // the heavy value i want to use everywhere!
val anotherHeavyObject = heavyProcessing2()
val items = Observable.fromIterable(listOfHundredsOfItems)
.map { doSomeWork(it, heavy) }
.map { doSomeWork(it, anotherHeavyObject) }
}
My attempts has so far not worked:
Create a wrapper around the existing function: The issue with this code is the Observable returned by start() does not get observed, so the doSomeWork doesn't actually get done. I only know this because I put breakpoints in at doSomeWork, and it never gets called.
fun startInBackground(): Single<Unit> {
return Single.fromCallable {
start()
}
}
I've been trying to find ways of 'unnesting' the inner Observable (inside the Single), as that's probably the issue here. The inner Observable is not being observed.
This RxJava stuff is very unintuitive even after reading the guide
Yes, it was related to Deferred-dependent. The example in the docs state:
Sometimes, there is an implicit data dependency between the previous sequence and the new sequence that, for some reason, was not flowing through the "regular channels". One would be inclined to write such continuations as follows:
AtomicInteger count = new AtomicInteger();
Observable.range(1, 10)
.doOnNext(ignored -> count.incrementAndGet())
.ignoreElements()
.andThen(Single.defer(() -> Single.just(count.get())))
.subscribe(System.out::println);
Actually, all I needed the caller to do is:
Single.defer { start() }.map { doMoreWork() }
instead of
start().map { doMoreWork() }

Kotlin difference between CoroutineScope and withContext

To change the thread in a function I use either CoroutineScope or withContext. I don't know's the difference, but with CourineScope I can also use a handler.
Examples:
private fun removeViews(){
CoroutineScope(Main).launch(handler){
gridRoot.removeAllViews()
}
}
private suspend fun removeViews(){
withContext(Main){
gridRoot.removeAllViews()
}
}
I call this function from a coroutine that works on background thread (IO). Is any more appropriate than the other?
These two are actually radically different and you just happen to have a use case where you don't experience the difference:
CoroutineScope(Main).launch(handler){
This launches a concurrent coroutine that goes on independently.
withContext(Main){
This is a function that completes only when the code inside it completes, and returns its result. This is the way you should be doing it.
The first approach, with CoroutineScope, has another deficiency in that it circumvents structured concurrency. You create an ad-hoc coroutine scope that has no parent and thus won't be automatically cleaned up if it takes a longer time to complete and your GUI is dropped (user navigates away from the current Activity).
You should actually never use the CoroutineScope(Main) idiom, I don't think there's a single instance where it would be appropriate. If you explicitly want to avoid structured concurrency, it is still better and cleaner to write
GlobalScope.launch(Main + handler) {
and has pretty much the same effect.
If you want a concurrent coroutine that fits into structured concurrency, use
fun CoroutineScope.removeViews() {
launch {
gridRoot.removeAllViews()
}
}
Note I removed the handler argument, a child coroutine ignores it because it forwards any failures to its parent coroutine, which is exactly what you want. The parent coroutine should have an exception handler installed.
Technically both are same but when it comes to use case both are different and has big impact on the different use cases so be careful while using them
Coroutine Scope:
CoroutineScope is a starting Point of Coroutine. CoroutineScope can have more than one coroutine within itself, which makes coroutine hierarchy.
Lets think, Parent has more than one children. Think CoroutineScope is a parent and this parent can have more than one child which are also coroutines. These childrens are known as job
private val coroutineScope = CoroutineScope()
coroutineScope(IO).launch{
val childOne = launch(Main){}
val childTwo = launch(Main){}
}
see that childOne and childTwo? why we need these? because we can't directly cancel the coroutine there is no such way the coroutine can be cancelled directly, either the coroutine gets completed or it gets failed. But what if we wanna cancel it? in such cases we need job. But thing to be notice here these job children are totally associated with parent. And Parent is (IO) and childrens are (Main), this parent is started in IO Disptacher but when it comes to those childrens they are gonna switch to (Main) and do their thing but the parent will still be at (IO) switching the Dispatcher of childrens not gonna effect parent.
But what happens if something wrong happens to either of the children,
in that case we will watch this summit:
https://www.youtube.com/watch?v=w0kfnydnFWI
This summit about coroutine exception and cancellation. watch it, its amazing...
withContext:
What is withContext?
withContext should be inside any Coroutine or suspend fun because withContext itself is a suspending function.
withContext is use to switch the context in different situation
but how?
suspend fun fetchFromNetworkAndUpdateUI() {
withContext(IO){
println("Some Fake data from network")
}
withContext(Main){
//updating Ui
//setting that Data to some TextView etc
}
}
see the code, we are fetching the data asynchronously from network cause we don't wanna block the MainThread and then we switch the context, why? cause we can't update UI related stuff in IoDispatcher that's we have change the context to main with withContext(main){} and update the UI.
and there are other use cases like liveData, we are fetching the value using retrofit using IoDispatcher then in next step we have to set it to the liveData by using withContext(main){} cause we can't observe liveData's value in background thread.
yeah, I hope this helps. comment if there is any question.
From the Antonio Leiva article about coroutines:
The coroutine context is a set of rules and configurations that define
how the coroutine will be executed
withContext is a function that allows you to easily change the context of a suspending function, in order to be sure that that function is executed in a particular thread (E.g. Thread from IO pool). To do so you can force a suspending function to execute its body within a particular thread pool, for example:
suspend fun getAuthenticationStatus(): AuthenticationStatus = withContext(Dispatchers.IO) {
when (val result = repository.getAuthenticationStatus()) {
is Result.Success -> result.data
is Result.Error -> AuthenticationStatus.Unauthorized
}
}
This way, even if you're calling this suspending function from a UI scope (MainScope), you are 100% sure that the suspending function is executed in a worker thread and you can update the UI with the returned result in the main thread, such as:
MainScope().launch {
userIdentityVM.getAuthenticationStatus().run {
when (this) {
is AuthenticationStatus.Authenticated -> {
// do something
}
is AuthenticationStatus.Unauthorized -> {
// do something else
}
}
}
}
To sum up, by using withContext you can make your suspending function "Main Safe".
The difference between scope and context is basically the intended purpose.
To launch a coroutine you normally use launch coroutine builder, defined as an extension function on CoroutineScope.
fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
// ...
): Job
The context specified as parameter on the coroutine scope is merged to coroutine scope by plus operator and takes precedence on the "default" context specified by coroutine scope. This way you can execute the code in a "parent" context. To go deep I suggest you this article by Roman Elizarov (Team Lead for Kotlin libraries #JetBrains).

Kotlin coroutines `runBlocking`

I am learning Kotlin coroutines. I've read that runBlocking is the way to bridge synchronous and asynchronous code. But what is the performance gain if the runBlocking stops the UI thread?
For example, I need to query a database in Android:
val result: Int
get() = runBlocking { queryDatabase().await() }
private fun queryDatabase(): Deferred<Int> {
return async {
var cursor: Cursor? = null
var queryResult: Int = 0
val sqlQuery = "SELECT COUNT(ID) FROM TABLE..."
try {
cursor = getHelper().readableDatabase.query(sqlQuery)
cursor?.moveToFirst()
queryResult = cursor?.getInt(0) ?: 0
} catch (e: Exception) {
Log.e(TAG, e.localizedMessage)
} finally {
cursor?.close()
}
return#async queryResult
}
}
Querying the database would stop the main thread, so it seems that it would take the same amount of time as synchronous code? Please correct me if I am missing something.
runBlocking is the way to bridge synchronous and asynchronous code
I keep bumping into this phrase and it's very misleading.
runBlocking is almost never a tool you use in production. It undoes the asynchronous, non-blocking nature of coroutines. You can use it if you happen to already have some coroutine-based code that you want to use in a context where coroutines provide no value: in blocking calls. One typical use is JUnit testing, where the test method must just sit and wait for the coroutine to complete.
You can also use it while playing around with coroutines, inside your main method.
The misuse of runBlocking has become so widespread that the Kotlin team actually tried to add a fail-fast check which would immediately crash your code if you call it on the UI thread. By the time they did this, it was already breaking so much code that they had to remove it.
Actually you use runBlocking to call suspending functions in "blocking" code that otherwise wouldn't be callable there or in other words: you use it to call suspend functions outside of the coroutine context (in your example the block passed to async is the suspend function). Also (more obvious, as the name itself implies already), the call then is a blocking call. So in your example it is executed as if there wasn't something like async in place. It waits (blocks interruptibly) until everything within the runBlocking-block is finished.
For example assume a function in your library as follows:
suspend fun demo() : Any = TODO()
This method would not be callable from, e.g. main. For such a case you use runBlocking then, e.g.:
fun main(args: Array<String>) {
// demo() // this alone wouldn't compile... Error:() Kotlin: Suspend function 'demo' should be called only from a coroutine or another suspend function
// whereas the following works as intended:
runBlocking {
demo()
} // it also waits until demo()-call is finished which wouldn't happen if you use launch
}
Regarding performance gain: actually your application may rather be more responsive instead of being more performant (sometimes also more performant, e.g. if you have multiple parallel actions instead of several sequential ones). In your example however you already block when you assign the variable, so I would say that your app doesn't get more responsive yet. You may rather want to call your query asynchronously and then update the UI as soon as the response is available. So you basically just omit runBlocking and rather use something like launch. You may also be interested in Guide to UI programming with coroutines.

How to perform operations in different thread then notify main thread in rxandroid?

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();

Categories

Resources