Coroutines
Coroutines are lightweight threads & creating coroutines doesn’t allocate new threads. They use predefined thread pools. It will enable writing asynchronous code in a synchronous way and makes the multitasking very easy. coroutines can run in parallel, wait for each other and communicate.
function fetchUser will look like below:
suspend fun fetchUser(): User {
return GlobalScope.async(Dispatchers.IO) {
// make network call
// return user
}.await()
}
fetchAndShowUser like below:
suspend fun fetchAndShowUser() {
val user = fetchUser() // fetch on IO thread
showUser(user) // back on UI thread
}
And the showUser function :
fun showUser(user: User) {
// show user
}
- Dispatchers: Dispatchers help coroutines in deciding the thread on which the work has to be done. There are majorly three types of Dispatchers which are as IO, Default, and Main.
- IO dispatcher is used to do the network or reading and writing from files. In short – any input and output.
- Default is used to do the CPU intensive work, such as sorting large lists, doing complex calculations.
- Main is the UI thread of Android. Dispatcher for performing UI-related events. For example, showing lists in a
RecyclerView
, updatingViews
and so on
EX : suspend fun async()
- suspend: Suspend function is a function that could be started, paused, and resume. Suspend functions are only allowed to be called from a coroutine or another suspend function. We can see the async function which includes the keyword suspend. So, in order to use that, we need to make our function suspend too.
So, the fetchAndShowUser can only be called from another suspend function or a coroutine. We can't make the onCreate function of an activity suspend, so we need to call it from the coroutines like below:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GlobalScope.launch(Dispatchers.Main) {
fetchAndShowUser()
}
}
Which is actually
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GlobalScope.launch(Dispatchers.Main) {
val user = fetchUser() // fetch on IO thread
showUser(user) // back on UI thread
}
}
showUser will run on UI thread because we have used the Dispatchers.Main to launch it.
There are two functions in Kotlin to start the coroutines which are as follows:
launch{}
async{}
Launch vs Async in Kotlin Coroutines
The difference is that the launch{}
does not return anything and the async{}
returns an instance of Deferred<T>
, which has an await()
function that returns the result of the coroutine like we have future in Java in which we do future.get() to the get the result.
In other words:
launch
: fire and forgetasync
: perform a task and return a result
We have a function fetchUserAndSaveInDatabase like below:
suspend fun fetchUserAndSaveInDatabase() {
// fetch user from network
// save user in database
// and do not return anything
}
Now, we can use the launch like below:
GlobalScope.launch(Dispatchers.Main) {
fetchUserAndSaveInDatabase() // do on IO thread
}
As the fetchUserAndSaveInDatabase does not return anything, we can use the launch to complete that task and then do something on Main Thread.
But when we need the result back, we need to use the async.
We have two functions which return User like below:
suspend fun fetchFirstUser(): User {
// make network call
// return user
}
suspend fun fetchSecondUser(): User {
// make network call
// return user
}
Now, we can use the async like below:
GlobalScope.launch(Dispatchers.Main) {
val userOne = async(Dispatchers.IO) { fetchFirstUser() }
val userTwo = async(Dispatchers.IO) { fetchSecondUser() }
showUsers(userOne.await(), userTwo.await()) // back on UI thread
}
Here, it makes both the network call in parallel, await for the results, and then calls the showUsers function.
There is something called withContext.
suspend fun fetchUser(): User {
return GlobalScope.async(Dispatchers.IO) {
// make network call
// return user
}.await()
}
withContext is nothing but another way of writing the async where we do not have to write await().
suspend fun fetchUser(): User {
return withContext(Dispatchers.IO) {
// make network call
// return user
}
}
But there are many more things that we should know about the withContext
and the await
.
Now, let's use withContext
in our async
example of fetchFirstUser
and fetchSecondUser
in parallel.
GlobalScope.launch(Dispatchers.Main) {
val userOne = withContext(Dispatchers.IO) { fetchFirstUser() }
val userTwo = withContext(Dispatchers.IO) { fetchSecondUser() }
showUsers(userOne, userTwo) // back on UI thread
}
When we use withContext
, it will run in series instead of parallel. That is a major difference.
The thumb-rules:
- Use
withContext
when you do not need the parallel execution. - Use
async
only when you need the parallel execution. - Both
withContext
andasync
can be used to get the result which is not possible with thelaunch
. - Use
withContext
to return the result of a single task. - Use
async
for results from multiple tasks that run in parallel.
Comments
Post a Comment