Android Kotlin Coroutines Explained - Complete Async Guide
In this tutorial, you'll learn Kotlin coroutines for Android async programming: Coroutine builders, dispatchers, structured concurrency, Exception Handling, and lifecycle-aware scopes.
What You'll Learn
Kotlin coroutines for Android async programming: coroutine builders, dispatchers, structured concurrency, exception handling, and lifecycle-aware scopes — Coroutines are the standard for async work on Android. They replace callbacks, simplify threading, prevent memory leaks, and integrate with Jetpack libraries.
Why It Matters
Coroutines are the standard for async work on Android. They replace callbacks, simplify threading, prevent memory leaks, and integrate with Jetpack libraries.
Real-World Use
A social media app uses coroutines to fetch the user's feed, download images in parallel, update the database, and post a status without blocking the UI or leaking activities.
Learning Path
flowchart LR
[Kotlin Basics] --> [Coroutines] --> [Retrofit] --> [Room]
style 2 fill:#4CAF50,color:#fff
Coroutine Builders and Dispatchers
class FeedViewModel : ViewModel() {
fun loadFeed() {
viewModelScope.launch {
val posts = withContext(Dispatchers.IO) { api.getFeed() }
_feed.value = posts
}
}
fun loadWithResult() {
viewModelScope.launch {
val result = async { fetchPosts() }
_feed.value = result.await()
}
}
}
Expected output: launch is fire-and-forget, async returns a Deferred value. withContext switches dispatchers for the IO operation.
Structured Concurrency
data class UserProfile(val user: User, val friends: List<User>, val recentPosts: List<Post>)
class ProfileViewModel : ViewModel() {
fun loadProfile(userId: String) {
viewModelScope.launch {
try {
val profile = coroutineScope {
val userDeferred = async { api.getUser(userId) }
val friendsDeferred = async { api.getFriends(userId) }
val postsDeferred = async { api.getRecentPosts(userId) }
UserProfile(userDeferred.await(), friendsDeferred.await(), postsDeferred.await())
}
_state.value = UiState.Success(profile)
} catch (e: Exception) {
_state.value = UiState.Error(e.message ?: "Error")
}
}
}
}
Expected output: All three API calls run in parallel. If any fails, all children are cancelled and the error is caught.
Lifecycle-Aware Collection
class LifecycleAwareFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.feed.collect { posts ->
adapter.submitList(posts)
}
}
}
}
}
Expected output: The coroutine auto-cancels when the lifecycle is destroyed, and restarts when it reaches STARTED again.
Common Errors
- CancellationException is normal - don't log it as an error; rethrow it in catch blocks
- Leaking coroutines - always use viewModelScope or lifecycleScope, never GlobalScope
- Using runBlocking in production - blocks the calling thread; use launch or async instead
- Calling suspend functions from non-coroutine code - wrap in viewModelScope.launch
- Not handling cancellation - closeable resources need try { } finally { resource.close() }
Practice Questions
What is the difference between launch and async?
How does structured concurrency prevent leaks?
When should you use Dispatchers.Default vs Dispatchers.IO?
What happens to child coroutines when the parent scope is cancelled?
How does withContext differ from async + await?
Challenge
Build a parallel image downloader: download 10 images simultaneously using async. Track progress with StateFlow
Real-World Task
Implement search-as-you-type: every keystroke triggers a debounced API search. Use flow.debounce(300).flatMapLatest in the ViewModel. Cancel the previous search when a new one arrives.
Frequently Asked Questions
{{< faq question="How many coroutines can I launch simultaneously?">}} Tens of thousands. Coroutines are lightweight threads using only a few hundred bytes each. {{< /faq >}}
{{< faq question="What is the difference between coroutines and RxJava?">}} Coroutines are Kotlin-native, simpler (sequential code), and integrate with Jetpack. RxJava has richer operators for complex pipelines. {{< /faq >}}
{{< faq question="Do coroutines automatically cancel when the app goes to background?">}} viewModelScope cancels when the ViewModel clears. lifecycleScope cancels when the LifecycleOwner is destroyed. Manual scopes need manual cancellation. {{< /faq >}}
Security Tip: Coroutines in background scopes can continue after the user leaves. Limit network calls in background coroutines and never perform sensitive operations without explicit user intent.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro