Coroutines & Background Work
The problem
The main thread (also called the UI thread) draws the screen and handles taps. If you run a long task on it — a network call, a big file read — the UI freezes and Android may show “App Not Responding”. The solution is to move that work to a background thread. Coroutines make this easy and readable.
suspend functions
suspend fun loadUser(): User {
delay(1000) // a non-blocking pause (won't freeze the UI)
return api.getUser(1)
}
Launching coroutines in the right scope
// in a ViewModel — cancelled automatically when the ViewModel clears
viewModelScope.launch {
val user = loadUser()
_name.value = user.name
}
Switching threads with Dispatchers
withContext(Dispatchers.IO) {
// heavy work: file, network, database
}
// back on the main thread automatically after this block
Doing things in parallel
suspend fun loadDashboard() = coroutineScope {
val user = async { api.getUser(1) }
val posts = async { api.getPosts() }
Pair(user.await(), posts.await()) // both run together
}
Guaranteed background jobs: WorkManager
Coroutines run while your app is alive. For work that must finish even if the app closes or the phone restarts — syncing, uploading, backups — use WorkManager.
class SyncWorker(ctx: Context, params: WorkerParameters) :
CoroutineWorker(ctx, params) {
override suspend fun doWork(): Result {
return try { syncData(); Result.success() }
catch (e: Exception) { Result.retry() }
}
}
val work = OneTimeWorkRequestBuilder<SyncWorker>()
.setConstraints(Constraints(requiredNetworkType = NetworkType.CONNECTED))
.build()
WorkManager.getInstance(context).enqueue(work)
Choosing the right tool
- Short async work tied to a screen → coroutines in
viewModelScope. - Guaranteed deferred work → WorkManager.
- Ongoing user-visible task (music, navigation) → a Foreground Service.
Summary: Never block the main thread. Use coroutines for in-app async work and WorkManager for jobs that must complete reliably in the background.