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.
Related
I exactly understand how are suspendCoroutine vs suspendCancellableCoroutine work in my samples. But im wondering why println("I finished") (line 13 - second line in viewscope block) executed after i had called viewScope.cancel(). I can fix it with isActive flag before this line but i don't want to check each line. What am i missing there. How i can cancel scope as well ? Thanks
import kotlinx.coroutines.*
import java.lang.Exception
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
fun main() {
val parentJob = Job()
val viewScope = CoroutineScope(Dispatchers.IO + parentJob)
viewScope.launch {
println(tryMe())
println("I finished")
}
Thread.sleep(2000)
viewScope.cancel()
Thread.sleep(10000)
}
suspend fun tryMe() = suspendCoroutine<String> {
println("I started working")
Thread.sleep(6000)
println("Im still working :O")
it.resume("I returned object at the end :)")
}
suspend fun tryMe2() = suspendCancellableCoroutine<String> {
println("I started working")
Thread.sleep(6000)
println("Im still working :O")
it.resume("I returned object at the end :)")
}
suspend fun tryMe3() = suspendCancellableCoroutine<String> {
it.invokeOnCancellation { println("I canceled did you heard that ?") }
println("I started working")
Thread.sleep(6000)
if (it.isActive)
println("Im still working :O")
it.resume("I returned object at the end :)")
}
If we just call cancel, it doesn’t mean that the coroutine work will just stop. If you’re performing some relatively heavy computation, like reading from multiple files, there’s nothing that automatically stops your code from running. Once job.cancel is called, our coroutine moves to Cancelling state.
Cancellation of coroutine code needs to be coperative
You need to make sure that all the coroutine work you’re implementing is cooperative with cancellation, therefore you need to check for cancellation periodically or before beginning any long running work. For example, if you’re reading multiple files from disk, before you start reading each file, check whether the coroutine was cancelled or not. Like this you avoid doing CPU intensive work when it’s not needed anymore.
All suspend functions from kotlinx.coroutines are cancellable: withContext, delay etc. So if you’re using any of them you don’t need to check for cancellation and stop execution or throw a CancellationException. But, if you’re not using them, to make your coroutine code cooperative by checking job.isActive or ensureActive()
Coroutine cancellation is co-operative
You should check whether the coroutine is still active before println("I finished") if you wish that statement not to be executed if the coroutine is canceled, like it follows:
if (isActive)
println("I finished")
Why is that?
Coroutines are not guranteed to be dispatched on another thread. So, while threads provide means to be aborted, which is implemented either at system–level or user–level in the runtime (e.g. the JVM, or ART), coroutines that are not backed by a thread couldn't be canceled anyhow, because the only thing that could be done is throw an exception, but that would abort the whole current execution context (i.e. thread), where other coroutines may be running.
Other answers talk about heavy computations, but that's plainly wrong. No matter what you're doing, whether computationally heavy or not — coroutines can't be forcefully canceled; their cancellation is only a request to be canceled, it's up to the coroutine body to handle cancellation requests using the CoroutineScope property isActive, which gets a boolean indicating whether the work the coroutine is carrying on should continue, if true; or be canceled, if false.
Using suspendCancellableCoroutine is working as expected. suspendCoroutine doesn't check for the cancellation state of the coroutineScope(internally uses safeContinuation) while suspendCancellableCoroutine does checks for the cancellation via CancellableContinuationImpl and it cancels the resume operation.
I have the following code in which I launch a coroutine to handle the retrieving of data and storing them into the local database:
private var lastRequestedPage = 1
private var isRequestInProgress = false
private val viewModelScope = CoroutineScope(viewModelJob + Dispatchers.Main)
// function that is called from several places
private fun requestAndSaveData(){
if(isRequestInProgress) return
isRequestInProgress = true
viewModelScope.launch{
withContext(Dispatchers.IO){
// 2 heavyweight operations here:
// retrieve some data via Retrofit & place it into the local data via Room Persistence Library
}
}
lastRequestedPage++
isRequestInProgress = false
}
Description of the code snippet
The network call and database operation is done based on a boolean value called isRequestInProgress. When that is false, it is set to true, the network & database operations can be started by the coroutine and after that the lastRequestedPage is incremented before we set isRequestInProgress again to false so that the whole process can be started again by the program from any place.
Note that lastRequestedPage is passed to the Retrofit network call function as argument since the web service from which the data comes uses Pagination (for the sake of brevity I left that out).
My question
Can I assume that this logic works with a coroutine like this ? Can I have some bad threading issues with a solution like this ? I am asking because I am new to the concept of coroutines and I was adapting this logic from another project of mine where I used listeners&callbacks with Retrofit to perform asynchronous work(whenever the Retrofit#onResponse method was called I incremented lastRequestedPage variable and set the isRequestInProgress to back to false).
Short answer: No, this won't work. It's incorrect.
When you call viewModelScope.launch { } or for that matter GlobalScope.launch { }, the block inside the launch gets suspended. And the program flow moves on to the next statement.
In your case, viewModelScope.launch { } will suspend the call to withContext(...) and move on to lastRequestedPage++ statement.
It will immediately increment the lastRequestedPage and toggle isRequestInProgress flag before even actually starting the request.
What you want to do is move those statements inside the launch { } block.
This is how the flow works.
Main thread
Suspend the block (launch call)
Proceed forward - Don't care about the suspended block - Make UI changes, etc.
To get a better sense of how it works, try this code.
Log.d("cor", "Hello, world!") // Main thread
GlobalScope.launch {
// Suspend this block. Wait for Main thread to be available
Log.d("cor", "I am inside the launch block") // Main thread
withContext(Dispatchers.IO) {
delay(100L) // IO thread
Log.d("cor", "I am inside the io block") // IO thread
}
Log.d("cor", "IO work is done") // Back to main thread
}
Log.d("cor", "I am outside the launch block") // Main thread
The output is
D/cor: Hello, world!
D/cor: I am outside the launch block
D/cor: I am inside the launch block
D/cor: I am inside the io block
D/cor: IO work is done
I'm putting together a simple demo app in Kotlin for Android that retrieves the title of a webpage with Jsoup. I'm conducting the network call using Dispatchers.Main as context.
My understanding of coroutines is that if I call launch on the Dispatchers.Main it does run on the main thread, but suspends the execution so as to not block the thread.
My understanding of android.os.NetworkOnMainThreadException is that it exists because network operations are heavy and when run on the main thread will block it.
So my question is, given that a coroutine does not block the thread it is run in, is a NetworkOnMainThreadException really valid? Here is some sample code that throws the given Exception at Jsoup.connect(url).get():
class MainActivity : AppCompatActivity() {
val job = Job()
val mainScope = CoroutineScope(Dispatchers.Main + job)
// called from onCreate()
private fun printTitle() {
mainScope.launch {
val url ="https://kotlinlang.org"
val document = Jsoup.connect(url).get()
Log.d("MainActivity", document.title())
// ... update UI with title
}
}
}
I know I can simply run this using the Dispatchers.IO context and providing this result to the main/UI thread, but that seems to dodge some of the utility of coroutines.
For reference, I'm using Kotlin 1.3.
My understanding of coroutines is that if I call launch on the Dispatchers.Main it does run on the main thread, but suspends the execution so as to not block the thread.
The only points where execution is suspended so as to not block the thread is on methods marked as suspend - i.e., suspending methods.
As Jsoup.connect(url).get() is not a suspending method, it blocks the current thread. As you're using Dispatchers.Main, the current thread is the main thread and your network operation runs directly on the main thread, causing the NetworkOnMainThreadException.
Blocking work like your get() method can be made suspending by wrapping it in withContext(), which is a suspending method and ensures that the Dispatchers.Main is not blocked while the method runs.
mainScope.launch {
val url ="https://kotlinlang.org"
val document = withContext(Dispatchers.IO) {
Jsoup.connect(url).get()
}
Log.d("MainActivity", document.title())
// ... update UI with title
}
Coroutine suspension is not a feature that magically "unblocks" an existing blocking network call. It is strictly a cooperative feature and requires the code to explicitly call suspendCancellableCoroutine. Because you're using some pre-existing blocking IO API, the coroutine blocks its calling thread.
To truly leverage the power of suspendable code you must use a non-blocking IO API, one which lets you make a request and supply a callback the API will call when the result is ready. For example:
NonBlockingHttp.sendRequest("https://example.org/document",
onSuccess = { println("Received document $it") },
onFailure = { Log.e("Failed to fetch the document", it) }
)
With this kind of API no thread will be blocked, whether or not you use coroutines. However, compared to blocking API, its usage is quite unwieldy and messy. This is what coroutines help you with: they allow you to continue writing code in exactly the same shape as if it was blocking, only it's not. To get it, you must first write a suspend fun that translates the API you have into coroutine suspension:
suspend fun fetchDocument(url: String): String = suspendCancellableCoroutine { cont ->
NonBlockingHttp.sendRequest(url,
onSuccess = { cont.resume(it) },
onFailure = { cont.resumeWithException(it) }
)
}
Now your calling code goes back to this:
try {
val document = fetchDocument("https://example.org/document")
println("Received document $document")
} catch (e: Exception) {
Log.e("Failed to fetch the document", e)
}
If, instead, you're fine with keeping your blocking network IO, which means you need a dedicated thread for each concurrent network call, then without coroutines you'd have to use something like an async task, Anko's bg etc. These approaches also require you to supply callbacks, so coroutines can once again help you to keep the natural programming model. The core coroutines library already comes with all the parts you need:
A specialized elastic thread pool that always starts a new thread if all are currently blocked (accessible via Dispatchers.IO)
The withContext primitive, which allows your coroutine to jump from one thread to another and then back
With these tools you can simply write
try {
val document = withContext(Dispatchers.IO) {
JSoup.connect("https://example.org/document").get()
}
println("Received document $it")
} catch (e: Exception) {
Log.e("Failed to fetch the document")
}
When your coroutine arrives at the JSoup call, it will set the UI thread free and execute this line on a thread in the IO thread pool. When it unblocks and gets the result, the coroutine will jump back to the UI thread.
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.
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