How to Use Coroutines in Android for Asynchronous Tasks
In Android, you use
coroutines by launching them within a CoroutineScope such as lifecycleScope or viewModelScope. Coroutines let you run long tasks like network calls without blocking the main thread, using launch or async builders inside suspend functions.Syntax
Coroutines run inside a CoroutineScope. You start a coroutine with launch for jobs that don't return a result, or async for jobs that return a value. Use suspend functions to mark code that can pause without blocking the thread.
CoroutineScope.launch { ... }: Starts a coroutine for background work.suspend fun: Defines a function that can suspend.withContext(Dispatchers.IO) { ... }: Switches to a background thread for heavy tasks.
kotlin
import kotlinx.coroutines.* fun example() { CoroutineScope(Dispatchers.Main).launch { val result = doWork() println(result) } } suspend fun doWork(): String { return withContext(Dispatchers.IO) { // simulate long task "Work done" } }
Output
Work done
Example
This example shows how to use lifecycleScope in an Android Activity to run a coroutine that simulates a network call without blocking the UI thread.
kotlin
import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(android.R.layout.simple_list_item_1) lifecycleScope.launch { val data = fetchData() println(data) // Prints "Data loaded" } } private suspend fun fetchData(): String { return withContext(Dispatchers.IO) { delay(1000) // simulate network delay "Data loaded" } } }
Output
Data loaded
Common Pitfalls
Common mistakes when using coroutines in Android include:
- Launching coroutines without a proper
CoroutineScope, which can cause memory leaks. - Blocking the main thread by calling
runBlockingor heavy work directly on the UI thread. - Not handling coroutine cancellation properly, leading to wasted resources.
Always use lifecycle-aware scopes like lifecycleScope or viewModelScope to avoid leaks.
kotlin
/* Wrong: launching coroutine without scope */ fun wrong() { GlobalScope.launch { // risky: no lifecycle awareness } } /* Right: use lifecycleScope in Activity or Fragment */ fun right(lifecycleScope: CoroutineScope) { lifecycleScope.launch { // safe: cancels with lifecycle } }
Quick Reference
Here is a quick summary of key coroutine components in Android:
| Concept | Description |
|---|---|
| CoroutineScope | Defines the lifecycle of coroutines, e.g., lifecycleScope, viewModelScope |
| launch | Starts a coroutine that does not return a result |
| async | Starts a coroutine that returns a result with Deferred |
| suspend function | Function that can suspend without blocking a thread |
| Dispatchers.Main | Runs coroutine on the main (UI) thread |
| Dispatchers.IO | Runs coroutine on a background thread for IO tasks |
| withContext | Switches coroutine context to another dispatcher |
Key Takeaways
Use lifecycle-aware CoroutineScopes like lifecycleScope to avoid memory leaks.
Mark long-running functions as suspend and call them inside coroutines.
Use Dispatchers.IO for background tasks to keep the UI responsive.
Avoid blocking the main thread by never running heavy work directly on it.
Handle coroutine cancellation properly to free resources when not needed.