So, I had this code running inside a "onBindViewHolder" recycler's adapter method:
launch(UI) {
val bitmapDrawable = loadLargeBitmapDrawable()
imageView.setImageDrawable(bitmapDrawable)
}
This was freezing my app for some seconds, locking my mainthread.
But then I changed to this:
launch { // <- I removed the "UI"
val bitmapDrawable = loadLargeBitmapDrawable()
launch(UI) { //Launch the UI coroutine inside the other
imageView.setImageDrawable(bitmapDrawable)
}
}
Why this is happening? The purpose of coroutines are to make things async inside the same thread (UI) right?
Someone can explain me why I had to run a UI coroutine inside another coroutine scope ?
The purpose of coroutines are to make things async inside the same thread (UI) right?
You ascribe more magic to coroutines than there really is. If your loadLargeBitmapDrawable() function is not suspendable, but simply occupies its thread until done, there is nothing Kotlin can do about it. When you said launch(UI), you ordered that function run on the UI thread.
Your second example executes in the CommonPool context (that's the default) and then posts a task to the UI thread; a more natural way to say it is like this (I use it in my code, with the exact same purpose as you):
launch(UI) {
val bitmapDrawable = withContext(CommonPool) {
loadLargeBitmapDrawable()
}
imageView.setImageDrawable(bitmapDrawable)
}
withContext will suspend the coroutine you launched on the UI thread, submit the heavyweight operation to the common threadpool, and then resume the coroutine on the UI thread with its result. Now you can push the bitmap to the imageView.
Related
I'm learning Android with Kotlin and I have learned that the recommended way to start coroutines without blocking the main thread is to do something like below
MainScope().launch {
withContext(Dispatchers.IO) {
// Do IO work here
}
}
But I was also wondering if the call below not would block the main thread because it's still using Dispatchers.IO
runBlocking(Dispatchers.IO) {
// Do IO work here
}
If you call runBlocking(Dispatchers.IO) from the main-thread, then the main-thread will be blocked while the coroutine finishes on the IO-dispatcher.
This is what the documentation says about this:
When CoroutineDispatcher is explicitly specified in the context, then
the new coroutine runs in the context of the specified dispatcher
while the current thread is blocked. If the specified dispatcher is an
event loop of another runBlocking, then this invocation uses the outer
event loop.
You can find the documentation here: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html
Dispatcher typically does not lead to an actual switching to another thread. In such scenarios,
* the underlying implementation attempts to keep the execution on the same thread on a best-effort basis.
https://github.com/Kotlin/kotlinx.coroutines/pull/3236/files
Consider the following code in kotlin.
val scope = CoroutineScope(Dispatchers.Main + Job())
scope.launch {
println("inside coroutine")
}
println("outside coroutine")
We create a coroutine in the Main(UI) thread and there is some code after the coroutine.
I know it doesn´t make much sense to do that in real code, but it´s just a theoretical question.
Considering that the coroutine runs in the Main thread, why println("outside coroutine") is ALWAYS executed first?
I would have expected that sometimes i would see first outside coroutine and other times, first inside coroutine, kind of like two threads.
Who (OS or Coroutines implementation) decides that the coe outside the coroutine is run first?
Considering that the coroutine runs in the Main thread, why println("outside coroutine") is ALWAYS executed first?
Let's imagine that your code instead was this:
someView.post {
println("inside post")
}
println("outside post")
Here, we create a Runnable (lambda expression) and pass that to post() on some View. post() says that the Runnable will be run() on the main application thread... eventually. That Runnable is put on the work queue that the Looper powering the main application thread uses, and it gets executed when that Runnable gets to the top of the queue (more or less — the details are messier IIRC but not important here).
But if you are executing this code on the main application thread, println("outside post") will always be printed first. The Runnable is placed onto the queue to be executed later, but you are still executing on the main application thread, and so even if the queue were empty, that Runnable will not run until you return control of the main application thread back to Android. So, after the call to post(), execution continues with println("outside post").
Under the covers, Dispatchers.Main is basically using post() (again, the details are more complicated but not too important for this discussion). So, when you launch() the coroutine, that lambda expression gets queued up to be executed eventually on the main application. But, you are already on the main application thread, so execution continues normally, and the println("outside post") gets printed before the coroutine gets a chance to do anything.
Suppose that your code instead was:
val scope = CoroutineScope(Dispatchers.Main + Job())
scope.launch {
println("inside coroutine")
}
scope.launch {
println("inside another coroutine")
}
Now you are in a situation where in theory either of those lines could be printed first. You are queuing up both lambda expressions, and it is up to the dispatcher to decide what to run on what thread at what point. In practice, it would not surprise me if "inside coroutine" is always printed first, as a simple implementation of Dispatchers.Main would use FIFO ordering in the absence of other constraints (e.g., a coroutine is blocked on I/O). However, you should not assume a particular order of invocation of those two coroutines.
I went through the Kotlin Coroutine, I understood how it works but I have a confusion between Kotlin coroutine & Android Async.execute() & Async await. The Kotlin coroutine runs in the background and does not block on the UI thread but the same thing happens when we start android AsyncTask(with the methods doInBackground onPostExecute and onProgressUpdate overridden), it also does the computation in a background thread and publishes the result on the UI thread.
Async-await returns a Deffered object means the result will obviously going to be returned in future.
Can Anyone Explain what's the difference between these.
Let's try to break this down:
The Kotlin coroutine runs in the background
A coroutine CAN run in the background
does not block on the UI thread
Let's talk about what a coroutine is:
A coroutine can be thought of as the code that gets passed to one of the coroutine builder functions i.e. launch {}
By definition when a coroutine is launched, suspend functions within it do not block the corresponding Thread when they are reached; they "pause" the coroutine.
When the suspension point is reached, it is as if you were telling the code to "call you back later" when the result is available; a suspension point can be though of as a callback.
Let's look at an example:
fun main() {
val job = MainScope().launch {
doSomeWork()
}
suspend fun doSomeWork() {/*expensive work goes here*/}
}
When doSomeWork() is reached the code will suspend the coroutine, i.e. the suspend modifier is indicating to the coroutine framework that it can go do some other coroutine related work and then come back to this point when doSomeWork() is done.
Since this coroutine is launched using MainScope() it will be launched in the main Thread. That is why I said that coroutines CAN run in a background Thread but not always do so. In this case it does not, but it still does not block the UI Thread.
In the other hand, AsyncTask was (it is deprecated as of API 30) a mechanism that performed a tasks in a background Thread and posted the result back to the UI Thread
For the difference between CoroutineScope.async{} and CoroutineScope.launch{} we can look at the return values for each. As I showed in the example. launch{} returns a Job which is a representation of the lifecycle of the coroutine itself. Using the Job you can cancel() or join() the coroutine; you have control over its lifecycle. As you mention, async{} returns a Deffered<T> which is a representation of a future value. When await() is called on the Deffered<T> the coroutine is suspended until the result is ready to be consumed.
I'm trying kotlinx.coroutines (version: 1.2.0). Here is a simple test code block:
GlobalScope.launch {
Logger.i("${Thread.currentThread()}, ${Looper.myLooper() == Looper.getMainLooper()}")
text_view.text = "test"
}
The printed log is:
Thread[DefaultDispatcher-worker-2,5,main], false
As the log shows, we are not on the Android main thread, i.e UI thread. However, the code above won't throw an exception after we set text to text_view on this worker thread, and "test" is set to text_view correctly. What's the reason?
Update 1:
Adding delay(10000L) before setText() will cause the exception while shorter time (like 1000L in my test for a debug run with cold startup) won't. So it seems like an Android issue. But still that question, what's the reason?
Update 2:
Now I realized this behavior is related to Android instead of kotlinx.coroutines. The code above is executed in onCreate() when ViewRootImpl may not have called performTraversals() or initialize all Views. In this situation, checkThread() before UI operation isn't called, either.
The default dispatcher, that is used when coroutines are launched in GlobalScope, is represented by Dispatchers.Default and uses shared background pool of threads, so launch(Dispatchers.Default) { ... } uses the same dispatcher as GlobalScope.launch { ... }.
Hence, When launch { ... } is used without parameters, it inherits the context (and thus dispatcher) from the CoroutineScope that it is being launched from.
In this case, it inherits the context of the main thread.
So, unless we define context & dispatcher, Coroutine will work on main thread creating new worker thread from DefaultDispatcher (in our case is main again).
Nothing to do with Kotlin coroutines really.
Even though you should not be calling UI functions from non-UI threads, not every Android UI function actually checks that you're on the UI thread. TextView#setText() is one of them and you can get away with calling it from a background thread without getting an exception.
GlobalScope.launch(Dispatchers.Main) {
mTvText?.text = "text" // exemple: set text in main thread
... // write also your code here
}
I have been wrapping my head around coroutines and I was wondering about the following code.
I have the following operation on my onCreate().
asyncJob = GlobalScope.launch(Dispatchers.Main) {
val name = async(Dispatchers.Default) { queryDevices() }.await()
mDeviceName.text = deviceName
}
Printing this out the order of execution seems to be before "name" is
on UI thread and after name is set, it is on the UI thread as well.
The queryDevicesMethod() is in a background thread as expected.
But I wanted to know what await() is actually doing when calling it on the UI thread?
Is it blocking theUI thread until await returns?
Coroutines will not block the thread on suspending. The Kotlin compiler generates a state machine that detaches and attaches the coroutines from the thread, see https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md.
In your case GlobalScope.launch(Dispatchers.Main) starts a new coroutine confined to the UI thread. Then async() starts a new coroutine confined to another dispatcher. The invocation of await() is a suspending function and will detach the first coroutine from the UI thread, waiting for the completion of the async-coroutine.
BTW:
You should not use async and await in one statement. That makes no sense.
What you really want is to run the queryDevices()-function from another dispatcher, but not asynchronously from the perspective of coroutines. In this case you should use withContext()