
Kotlin Coroutines: Hướng Dẫn Lập Trình Bất Đồng Bộ Trên Android
Kotlin Coroutines là công cụ mạnh mẽ giúp lập trình viên viết code bất đồng bộ một cách dễ đọc và hiệu quả. Bài viết này hướng dẫn chi tiết từ cơ bản đến nâng cao, kèm ví dụ thực tế cho Android.
Coroutines Là Gì?
Coroutines là một cơ chế lập trình cho phép hàm suspend tạm dừng và tiếp tục thực thi mà không chặn thread. Khác với thread truyền thống, coroutines nhẹ hơn rất nhiều — hàng nghìn coroutine có thể chạy trên vài thread vật lý. Điều này giúp tiết kiệm tài nguyên bộ nhớ đáng kể so với việc tạo nhiều thread riêng lẻ.
Trong lập trình Android, việc thực hiện các tác vụ mạng hoặc cơ sở dữ liệu trên thread chính (UI thread) sẽ gây ra lỗi Application Not Responding (ANR). Coroutines giải quyết vấn đề này bằng cách cho phép lập trình viên viết code bất đồng bộ trông giống code đồng bộ, giúp code dễ đọc, dễ bảo trì và dễ kiểm thử hơn.
Cơ chế suspend và resume cho phép coroutine tạm dừng tại các điểm kiểm soát mà không chiếm dụng thread. Khi tác vụ hoàn tất, coroutine tự động tiếp tục từ vị trí đã dừng.

Cấu Trúc Cơ Bản
Để dùng coroutines, bạn cần dependency kotlinx-coroutines-core. Hai thành phần chính:
- launch: Khởi chạy coroutine không trả về kết quả. Dùng cho các tác vụ “fire and forget” như gửi analytics, ghi log.
- async: Khởi chạy coroutine trả về kết quả qua
Deferredinterface. Dùng khi cần chờ kết quả từ nhiều tác vụ song song.
Ngoài ra, CoroutineScope quản lý vòng đời của coroutines. Khi scope bị hủy, tất cả coroutines con tự động hủy theo — đây là “structured concurrency”. Scope có thể là viewModelScope trong ViewModel, lifecycleScope trong Activity/Fragment, hoặc custom scope.
Ví Dụ Đơn Giản
Đoạn code dưới đây tạo 2 coroutine chạy song song và chờ kết quả:
fun main() = runBlocking {
val job1 = launch { delay(1000L); println("Task 1") }
val job2 = launch { delay(500L); println("Task 2") }
joinAll(job1, job2)
}
Kết quả: Task 2 in ra trước (sau 500ms), sau đó Task 1 (sau 1000ms tổng). joinAll chặn cho đến khi cả hai hoàn thành. runBlocking tạo scope block thread hiện tại.
Dispatchers — Quản Lý Thread
Coroutines dùng Dispatcher để quyết định thread thực thi, ảnh hưởng trực tiếp đến hiệu suất app:
- Dispatchers.Main: UI thread (Android main thread). Dùng để cập nhật giao diện, tương tác user.
- Dispatchers.IO: Thao tác mạng, database, file I/O. Pool thread tối ưu cho tác vụ blocking I/O với tối đa 64 thread.
- Dispatchers.Default: Tính toán nặng CPU. Pool thread bằng số CPU cores, phù hợp cho sorting, filtering large data.
- Dispatchers.Unconfined: Bắt đầu trên thread hiện tại, tiếp tục trên thread gọi suspend function. Hiếm khi dùng trong production.
Việc chọn đúng dispatcher rất quan trọng cho hiệu suất app. Sai dispatcher có thể gây block UI hoặc lãng phí tài nguyên. Ví dụ, dùng Dispatchers.Main cho tác vụ mạng sẽ crash app.

Hủy Coroutine (Cancellation)
Coroutine hỗ trợ hủy có cấu trúc (structured concurrency). Khi parent scope bị hủy, tất cả child coroutine tự động hủy theo:
val scope = CoroutineScope(Dispatchers.IO + Job())
val job = scope.launch {
while (isActive) { /* chạy */ }
}
scope.cancel() // Hủy cả scope và job
isActive là biến check trong coroutine, cho phép dừng loop một cách an toàn. Ngoài ra, withTimeout(1000) { ... } và withTimeoutOrNull(1000) { ... } cho phép đặt thời gian chờ cho tác vụ, trả về null nếu quá hạn.
Flow: Xử Lý Dữ Liệu Streaming
Flow là API reactive cho coroutines, phát ra nhiều giá trị theo thời gian. Khác với LiveData, Flow hoạt động hoàn toàn với coroutines và không phụ thuộc vào Android lifecycle.
flow {
emit("A")
delay(500)
emit("B")
}.collect { value -> println(value) }
Flow có 3 loại chính: StateFlow (giá trị hiện tại, luôn có, dùng cho UI state), SharedFlow (phát nhiều giá trị, không lưu trữ, dùng cho events), và Flow (cold stream, chỉ phát khi collect).
Có thể biến đổi Flow bằng các operator như map, filter, flatMapLatest, combine. Flow tự động cancel khi collector bị hủy.
Error Handling
Coroutine exception handling khác với thread truyền thống. Mặc định, exception trong launch không được catch mà sẽ crash application. Dùng try-catch trong async hoặc CoroutineExceptionHandler:
val handler = CoroutineExceptionHandler { _, exception ->
println("Error: ${exception.message}")
}
val scope = CoroutineScope(Dispatchers.IO + handler)
scope.launch { throw RuntimeException("Oops") }
supervisorScope cho phép child coroutine lỗi độc lập mà không hủy các sibling coroutines khác.
Kết Luận
Kotlin Coroutines giúp code bất đồng bộ trở nên đơn giản và an toàn. Với structured concurrency, Suspend functions, Dispatchers, Flow, và error handling, lập trình viên Android có thể viết app hiệu suất cao mà không bị rối với callback chains. Đọc tài liệu chính thức để tìm hiểu sâu hơn.
