How Kotlin coroutines are scheduled - android

I've been reading a lot of articles and watching a lot of videos on Kotlin co-routines lately and, despite my efforts, I still can't make sense of them in my head.
I think I've finally found a way to illustrate my problem:
class MyViewModel() : CoroutineScope {
override val coroutineContext = Dispatchers.Main + Job()
fun foo() = launch(handler) {
Log.e("test", "A")
}
}
class MainActivity : Activity() {
override fun onCreate() {
MainViewModel().foo()
Log.e("test", "B")
}
}
The output of this is:
E/test: B
E/test: A
And I don't get how this can be the case, I'm using only one thread (the main thread). If my code executes sequentially, by the time I reach the line log(B)... log(A) should have already be printed.
Does the coroutines library use other threads internally to accomplish this? This is the only explanation I could come up with but haven't found anything saying so in the docs.
PS: Sorry for throwing android into the mix but this code:
fun main() {
GlobalScope.launch(Dispatchers.Unconfined) { // launch new coroutine in background and continue
print(Thread.currentThread().name + "World!") // print after delay
}
(0 .. 1000).forEach { print(".") }
}
seems to work as expected and prints:
main #coroutine#1World!...........................
because 1 thread == sequential work
Hope my question makes sense, thanks for reading!

Under the hood, the Main dispatcher uses a Handler to post a Runnable to the MessageQueue. Basically, it’ll get added to the end of the event queue. This means it will execute soon, but not immediately. Hence why “B” gets printed before “A”.
You can find more information in this article.
EDIT by OP (read the article above before reading this):
Just wanted to clarify why the android example above was working fine, in case someone is still wondering.
fun main() {
GlobalScope.launch(Dispatchers.Unconfined) { // launch new coroutine in background and continue
print(Thread.currentThread().name + "World!") // print after delay
}
(0 .. 1000).forEach { print(".") }
}
We're setting GlobalScope to use the UNCONFINED dispatcher, and this dispatcher has isDispatchNeeded set to false.
false means "schedule in the current thread", and that's why we see the logs printing sequentially. UNCONFINED should not be used in regular code.
All other dispatchers have isDispatchNeeded set to true even the UI dispatcher. see: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/is-dispatch-needed.html
(btw GlobalScope uses the Default dispatcher if we don't specify one)

Related

How to understand and improve async-await in coroutines?

I would like to improve my async-await in coroutines.
This is my solution
val coroutineContext: CoroutineContext
get() = job + Dispatchers.IO
[...]
lifecycleScope.launch(coroutineContext) {
async {
client = viewModel.getClientItem(clientId)
}.await()
if (inEditMode != null) {
changedEditMode = true
}
[...]
#Marko Topolnik wrote in Why does this coroutine block UI Thread?
Note: this post dates back to the pre-release version of coroutines. I updated the names of dispatchers to match the release version.
runBlocking is not the way to start a coroutine on the UI thread because, as its name says, it will block the hosting thread until the coroutine is done. You have to launch it in the Main context and then switch to the Default context for the heavyweight operation. You should also drop the async-await pair and use withContext:
button1.setOnClickListener {
launch(Main) {
withContext(Default) {
Thread.sleep(5000L)
}
textView1.text = "Done! UI was not blocked :-)"
}
}
withContext will suspend the coroutine until done and then resume it in the parent context, which is Main.
Why doing it with Main Dispatchers and then withContext using Default dispatchers is betters solution than async. It will also block main thread and work the same what is the difference. And how to handle this approaches?
Greetings
EDIT:
My other solution is this below or withContext(Dispatchers.IO)
lifecycleScope.launch(Dispatchers.Main) {
withContext(Dispatchers.Default) {
client = viewModel.getItem(clientId)
}
[...]
}
Now as I read I do not use runBlocking and don't block main thread?
In that configuration it works the same as with async-await.
In your opinin that's better solution
?
Why doing it with Main Dispatchers and then withContext using Default dispatchers is betters solution than async. It will also block main thread and work the same what is the difference
withContext doesn't block, it suspends. The terminology might should similar but the behaviour is very different. While Thread.sleep is executing on the Default dispatcher (and thus on one of this dispatcher's threads), the main thread can keep running other code. The coroutine launched via launch(Main) here is in a suspended state, which means the main dispatcher knows it can execute other things instead. The linked answer here explains why runBlocking was a bad idea, because no matter what you launch in it runBlocking has to block the current thread while it's executing coroutines inside.
Now back to your code. Note that async { doStuff() }.await() makes little sense. It is equivalent to simply doStuff().
Another thing is that you're passing a job explicitly to launch via the context here. This doesn't seem right, and could lead to problems. If you want to use Dispatchers.IO, you don't need to pass a job too.
That said, even Dispatchers.IO seems like a stretch here. You should instead make viewModel.getClientItem a suspend function (if not already), and leave the responsibility of using the right dispatcher to this function. Maybe you don't even need IO at all here, or maybe it's an HTTP or DB call that already has its own thread pool anyway.
So I would replace your whole code with:
lifecycleScope.launch {
client = viewModel.getClientItem(clientId)
if (inEditMode != null) {
hangedEditMode = true
}
}

How to use Thread.sleep() in Kotlin

This comes from near the end of the codelab found here:
Intro to debugging - Debugging example: accessing a value that doesn't exist
This is all inside the MainActivity.kt file
Here's my onCreate
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val helloTextView: TextView = findViewById(R.id.division_textview)
helloTextView.text = "Hello, debugging!"
division()
}
//...
Here's the division function provided by Google, but with 2 changes I'll explain in a moment...
fun division() {
val numerator = 60
var denominator = 4
repeat(4) {
Thread.sleep(3000)
findViewById<TextView>(R.id.division_textview).setText("${numerator / denominator}")
Log.v(TAG, "${numerator / denominator}")
denominator--
}
}
The instructions make it seem like they expect sleep() to accept seconds, but AS indicates it expects millis, so I changed their 3 to 3000. I added Log.v(TAG, "${numerator / denominator}") to see what was happening because it wasn't doing as expected.
The point of this was to have the emulator create a gif of the quotients being updated. This is supposed to be helpful when debugging.
Nothing displays on screen, not even the app's name, until the repeat() finishes.
The logs happen in 3 second intervals, as expected.
Why is the layout waiting, and how do I make it update on each iteration of the loop?
I honestly have no idea what that Codelab is doing, based off the code they provide. The app isn't going to render anything (not the layout, not any changes to the layout) before onCreate finishes, and onCreate won't finish until it's run all its code, including that repeat block in the division function it calls.
division isn't starting any worker threads, so all Thread.sleep is doing is blocking the main thread - it's hanging the app. And you're right, sleep does take a millis value, not seconds - I get the feeling they didn't actually run this code, it's full of other mistakes and inconsistencies that honestly made it hard to work out what you were meant to be doing. Change which Log.d call? The ones in onCreate? (They actually mean the Log.v call in division, I assume)
Here's how you'd use a thread in Kotlin - you need to create a new one (so you're off the main thread, so it can actually finish creating the activity and run the UI):
fun division() {
// create a new thread (and start it immediately)
thread(start=true) {
repeat(4) { i ->
Thread.sleep(3000L)
// assuming you've done the ``findViewById`` and assigned it to a variable
runOnUiThread { divisionTextView.text = "$i" }
}
}
}
That's just updating with the current repeat number (i) for brevity, but the important things are:
you're creating a new thread to do the work (and sleeping)
you're using runOnUiThread (which is a method on the Activity) to do the text updating on the main/UI thread (same thing). If you try to touch the UI from another thread it will crash
Another way you can do that is to post a runnable to the UI thread through a view, may as well use that TextView:
divisionTextView.post { divisionTextView.text = "$i" }
Coroutines would be a better idea here (you can run them on the main thread without it blocking, so you don't have to worry about switching threads to update the UI, or any thread safety stuff) but that's the basics of doing a thread. Genuinely have no idea what's going on in that codelab.
Nothing displays on screen, not even the app's name, until the repeat() finishes. The logs happen in 3 second intervals, as expected. Why is the layout waiting?
It is due to Activity life cycle
When the activity is in the Created state, you can't see the UI.
When the activity is in the Started state, UI becomes visible but you can't interact with it.
When the activity is in the Resumed state, UI is visible and you are able to interact with it.
Since you're calling the division() function in the onCreate() callback, you won't see the activity UI.
how do I make it update on each iteration of the loop?
One way of doing that is to run your division() function in a new thread. By doing so, the main tread displays the UI (you'll be able t see it). Then use runOnUiThread to update your division_textview text.
fun division() {
val numerator = 60
var denominator = 4
thread(start=true) {
/* The loop only repeat 4 times to avoid a crash*/
repeat(4) {
Log.v(TAG, "$denominator")
// Set division_textview text to the quotient.
runOnUiThread { findViewById<TextView>(R.id.division_textview).setText("${numerator / denominator}") }
// Wait for 1 second
Thread.sleep(1000L)
denominator--
}
}
}
You can use Corountines. delay function is very easy to use
lifecycleScope.launch {
myTextView.text = "Starting"
delay(1000L)
myTextView.text = "Processing"
delay(2000L)
myTextView.text = "Done"
}

Get Value from Object to Activity/Fragment in Kotlin [duplicate]

I'm writting my first app in Kotlin, so I'm pretty new to this. One of the functions it performs is to read something from an API, and then update the screen based on the result.
I have tried lots of things with the coroutines, but nothing seems to work.
For example, I have something like this:
private fun readAPI() {
runBlocking {
fun rAPI() = async {
val api = "..."
result = URL(api).readText()
}
println(tag, "Result: " + rAPI().await())
}
}
And lots of different approaches. Nothing seems to work. In the above case I'm getting an exception "android.os.NetworkOnMainThreadException".
The only thig that has worked so far, is something using OkHttp3 as described here: https://rstopup.com/como-hacer-una-solicitud-a-la-api-de-kotlin.html (it's in Spanish, but you'll get the idea), and this works, it brings the API response, I parse it, fill in my sqlite3 database and so on. But, since I don't know when the API ends, I can't update the screen controls. And if I try to do it serially, I get an exception that only the thread which started the activity is the one that can update the activity or something like that.
I've seen, and follow LOTS of tutorials that talks about suspend functions, launch, etc., and they try to mimick an API call with delay(), those tutorials work perfectly, until I try to do a real API call.
So, can you point me to a full example on how to call an API with Kotlin, and then update some screen elements?
EDIT
I'm editing changing the fun by val:
runBlocking {
val rAPI = async {
val api = "..."
URL(api).readText()
}
Log.w(tag, rAPI.await())
}
I got the "android.os.NetworkOnMainThreadException" exception.
Since you want to use coroutine-async ways, you must tell main thread to waiting for you. you need to use suspend function or block to do this.
GlobalScope.launch {
suspend {
Log.d("coroutineScope", "#runs on ${Thread.currentThread().name}")
delay(10000)
withContext(Dispatchers.Main) {
Log.d("coroutineScope", "#runs on ${Thread.currentThread().name}")
}
}.invoke()
}
result log
09:36:09.500 D/: #runs on DefaultDispatcher-worker-1
// 10 seconds later
09:36:19.678 D/: #runs on main
I think this should do the trick.
However I suggest you to understand how to use OkHttp/Volley by passing callbacks(with onSuccess and onFail or something) into that.
Or Retrofit2 with RxJavato handle many of these issues.
EDIT
For the Module with the Main dispatcher had failed to initialize error, replace the withContext() line with this
withContext(Handler(Looper.getMainLooper()).asCoroutineDispatcher())
EDIT
Now don't use RxJava, use liveData/LiveEvent to implement the observer pattern
In Kotlin you can convert callbacks to coroutines as described in this codelab https://codelabs.developers.google.com/codelabs/kotlin-coroutines/#6

Save file with Kotlin Coroutines in Android shows inappropiate blocking method call

I created a function for saving an image to the internal files directory. I execute this function in the lifecycleScope provided by Android like following:
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launch {
saveImage(id, proxyEntity)
}
}
This is my function where I want to save the image.
private suspend fun saveImage(
id: Long,
proxyEntity: ProxyEntity,
) {
val entitiesDirectory = File(filesDir, "local/entities")
if (false == entitiesDirectory.isDirectory) {
entitiesDirectory.mkdirs()
}
if (null != selectedImage) {
withContext(Dispatchers.IO) {
val entityFile = File(entitiesDirectory, "$id")
val fileOutputStream = FileOutputStream(entityFile)
//selectedImage is of type Bitmap
fileOutputStream.write(ImageUtil.convertBitmapToByteArray(selectedImage!!))
fileOutputStream.close()
proxyEntity.imagePath = "local/entities/${id}"
}
}
}
The code itself is working, but my question is, why Android-Studio still shows me "Inappropriate blocking method call" and if I am doing anything wrong.
EDIT:
The message is appearing on FileOutputStream(entityFile) and the write and close function.
UPDATE: After reading Tenfour04's comment
At first I was hesitant, because in theory, suspend doesn't imply anything about the context. But like most things in life, after thinking more about it (and sleeping), I think it makes sense in a practical way to make some controlled and consistent choices. Among them, to ensure:
That you can replace the Dispatcher in a Unit Test (this means: don't hardcode, instead, inject it so you can replace it).
E.g.:
instead of:
...launch(Dispatchers.IO) { ... }
Do:
class XYZ(private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default) {
suspend fun xyz() = withContext(defaultDispatcher) { ... }
You get the idea.
Suspend functions should be safe to call from main thread
This is an interesting choice but I understand why. And I will start doing this in all my code where I can.
Source: Suspend functions should be safe to call from the main thread.
I suggest you read the Coroutines Best Practices recently updated/published by Google. We may all have our disagreements and use a different patterns here and there, but overall, I'd do what Google suggests; in the end, it's their platform and the closer you are to their code, the easier will be to deal with the incessant changes and deprecation that happen in modern development these days.
UPDATE: I haven't had coffee.
Ok, after re-reading your code, I noticed you do specify the Coroutine Context.
That being said, I'd make the context assignment the way I did it (in the viewmodel launch's call).
You don't want your code to "care" which context is used on, instead let the viewmodel decide what's best.
Original Response
The I/O operations on the main thread are a bad idea (if not forbidden by some APIs), so you need to change the scheduler
Your code:
lifecycleScope.launch {
saveImage(id, proxyEntity)
}
Is running on the Default scheduler (likely Main Thread).
Try instead to use the I/O one:
lifecyleScope.launch(Dispatchers.IO) {
saveImage(id, proxyEntity)
}

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.

Categories

Resources