Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to use withContext?

Currently my code looks like this i have a ViewModel that calls the repository to do some background computations and return a result.
ViewModel function is run with viewModelScope.launch(Dispatchers.IO) and then the repository one is a suspend function.
Do I have to use return withContext{} to ensure that everything will be done sequentially? I checked and it is indeed sequential, but in the documentation i found that it doesn't have to be?

like image 549
baltekg Avatar asked Sep 25 '20 08:09

baltekg


People also ask

What is withContext () and how it differs from async?

Use withContext when you do not need the parallel execution. Use async only when you need the parallel execution. Both withContext and async can be used to get the result which is not possible with the launch . Use withContext to return the result of a single task.

What is withContext in Android?

withContext is a scope function that allows us to create a new cancelable coroutine. If we pass a CoroutineContext arg, withContext merges the parent context and our arg to create a new CoroutineContext, then executes the coroutine within this merged context.

What is Kotlin withContext?

withContext(Dispatchers.IO) moves the execution of the coroutine to an I/O thread, making our calling function main-safe and enabling the UI to update as needed. makeLoginRequest is also marked with the suspend keyword. This keyword is Kotlin's way to enforce a function to be called from within a coroutine.

What is return withContext?

withContext. It's a suspend function that runs a block of code in the specified coroutine context, suspends the current coroutine until the execution has completed and returns the result.


1 Answers

Let's dissect the viewModelScope first from it's source code. It uses a custom implementation of CouroutineScope. The context comprises of a SupervisorJob and a Dispatchers.Main dispatcher. This ensures that the coroutine is launched on the main thread and it's failure doesn't affect other coroutines in the scope.

CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))

Couple of examples worth exploring.

viewModelScope.launch {
    Log.d("ViewModel", "Just viewModelScope: ${Thread.currentThread().name}")
}
// Output: Just viewModelScope: main

viewModelScope.launch(Dispatchers.IO) {
    Log.d("ViewModel", "IO viewModelScope: ${Thread.currentThread().name}")
}
// Output: IO viewModelScope: DefaultDispatcher-worker-3

viewModelScope.launch {
    Log.d("ViewModel", "viewModelScope thread: ${Thread.currentThread().name}")
    withContext(Dispatchers.IO) {
        delay(3000)
        Log.d("ViewModel", "withContext thread: ${Thread.currentThread().name}")
    }
    Log.d("ViewModel", "I'm finished!")
}
// Output: 
// viewModelScope thread: main
// withContext thread: DefaultDispatcher-worker-4

I checked and it is indeed sequential, but in the documentation i found that it doesn't have to be.

withContext() is a suspending operation and the coroutine will suspend till it's completion and then proceed ahead. That is apparent from the third example above.

In summary, viewModelScope will use main thread to execute a coroutine whose cancellation won't affect other couroutines. Use withContext when you want to do heavy task off of main thread in a suspending fashion; dispatch it using an appropriate dispatcher. Kotlin Coroutine guide is worth a read.

Edit:

Consider the below code as a single unit of execution. This illustrates the fact that when using withContext(), the caller thread is suspending, but it is not blocked, which allows it to go ahead and pick up some other pending work. The interleaving of the output loggers is of interest to us.

viewModelScope.launch {
    Log.d("ViewModel", "viewModelScope thread: ${Thread.currentThread().name}")
    withContext(Dispatchers.IO) {
        delay(3000)
        Log.d("ViewModel", "withContext thread: ${Thread.currentThread().name}")
    }
    Log.d("ViewModel", "I'm finished!")
}

viewModelScope.launch {
    Log.d("ViewModel", "I'm not blocked: ${Thread.currentThread().name}")
}

// Output: 
// viewModelScope thread: main
// I'm not blocked: main
// withContext thread: DefaultDispatcher-worker-2
// I'm finished!
like image 194
Siddharth Kamaria Avatar answered Nov 15 '22 00:11

Siddharth Kamaria