Coroutine Builder — các hàm của Kotlin Coroutines tạo và chạy coroutine, xác định cách chúng thực thi. Các trình xây dựng launch, async, runBlocking và produce bao phủ nhiều kịch bản khác nhau: từ tác vụ nền đến tính toán song song có trả về kết quả. Theo JetBrains, 2024, Coroutine Builder là nền tảng của mô hình coroutine, cung cấp khả năng đồng thời có cấu trúc và quản lý vòng đời.
Những điểm chính
Coroutine Builder là một hàm mở rộng của Kotlin nhận CoroutineScope và một khối suspend, tạo và chạy một coroutine mới. Mỗi trình xây dựng xác định cách coroutine sẽ thực thi: có hoặc không trả về kết quả, có chặn luồng hoặc bất đồng bộ. Các trình xây dựng là điểm vào của mô hình coroutine trong ngôn ngữ.
Tất cả các trình xây dựng hoạt động thông qua CoroutineScope, quản lý vòng đời của các coroutine con. Khi một phạm vi bị hủy, tất cả coroutine được chạy qua nó sẽ tự động bị hủy — đây là nguyên tắc của đồng thời có cấu trúc. Cách tiếp cận này ngăn chặn rò rỉ coroutine và đảm bảo kết thúc có thể dự đoán trước.
import kotlinx.coroutines.*
fun main() = runBlocking {
// Các trình xây dựng chạy bên trong CoroutineScope
val job = launch {
delay(1000L)
println("Thế giới!")
}
println("Xin chào,")
job.join()
}
Kotlin cung cấp bốn trình xây dựng coroutine tích hợp: launch, async, runBlocking và produce. Mỗi loại có kiểu trả về và phạm vi ứng dụng riêng. Đối với phát triển di động Android, các trình xây dựng chính là launch và async — chúng hoạt động không chặn và tích hợp với các thành phần kiến trúc.
| Trình xây dựng | Kiểu trả về | Chặn luồng | Kịch bản |
|---|---|---|---|
| launch | Job | Không | Tác vụ fire-and-forget |
| async | Deferred<T> | Không | Tính toán song song |
| runBlocking | T | Có | Kiểm thử, hàm main |
| produce | ReceiveChannel<E> | Không | Truyền phát (không khuyến khích) |
Mỗi trình xây dựng chấp nhận các tham số bổ sung: CoroutineStart (chiến lược khởi chạy), CoroutineContext (bộ điều phối, ngoại lệ) và một khối code có tên. Theo mặc định, coroutine bắt đầu ngay lập tức (CoroutineStart.DEFAULT).
launch là trình xây dựng được sử dụng nhiều nhất trong phát triển Android. Nó chạy một coroutine không trả về kết quả và trả về đối tượng Job để quản lý vòng đời của nó. Đây là lựa chọn lý tưởng cho các thao tác chỉ cần hiệu ứng phụ: lưu vào cơ sở dữ liệu, gửi phân tích, cập nhật giao diện.
Trình xây dựng launch chấp nhận CoroutineScope, một CoroutineContext tùy chọn và một khối suspend. Job được trả về cho phép hủy coroutine, chờ hoàn thành hoặc kiểm tra trạng thái.
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
val job: Job = scope.launch(CoroutineStart.LAZY) {
val data = fetchFromNetwork()
saveToDatabase(data)
}
job.start()
job.join()
Tham số CoroutineStart.LAZY hoãn thực thi cho đến khi gọi start() hoặc join() một cách tường minh. Điều này hữu ích cho khởi tạo trễ và thực thi có điều kiện. Để thực thi tức thời tiêu chuẩn, sử dụng CoroutineStart.DEFAULT hoặc bỏ qua tham số.
async là trình xây dựng trả về Deferred<T> — một lời hứa bất đồng bộ về kết quả. Gọi await() tạm dừng coroutine cho đến khi nhận được kết quả mà không chặn luồng. Đây là cơ chế chính cho các tác vụ song song trong coroutine Kotlin.
async đặc biệt hiệu quả khi bạn cần thực thi nhiều thao tác độc lập đồng thời. Không giống như gọi tuần tự các hàm suspend, async chạy coroutine song song, giảm tổng thời gian thực thi.
suspend fun fetchUserData(): UserData {
val deferred1 = CoroutineScope(Dispatchers.IO).async { api.getProfile() }
val deferred2 = CoroutineScope(Dispatchers.IO).async { api.getSettings() }
val deferred3 = CoroutineScope(Dispatchers.IO).async { api.getNotifications() }
return UserData(
profile = deferred1.await(),
settings = deferred2.await(),
notifications = deferred3.await()
)
}
Deferred kế thừa từ Job, do đó async hỗ trợ tất cả các thao tác vòng đời: hủy bỏ, chờ hoàn thành, xử lý ngoại lệ. Khi một phạm vi bị hủy, các coroutine Deferred con tự động bị hủy.
runBlocking là trình xây dựng duy nhất chặn luồng hiện tại cho đến khi coroutine hoàn thành. Nó tạo một CoroutineScope mới và chạy coroutine được truyền vào, chặn luồng gọi. Nó được sử dụng trong các điểm vào main(), trong kiểm thử và khi tích hợp với code chặn.
runBlocking được biện minh trong ba kịch bản: điểm vào ứng dụng (main), kiểm thử đơn vị các hàm suspend và tích hợp với các thư viện dựa trên callback không thể dùng suspend. Trong code Android sản xuất, sử dụng runBlocking trên luồng chính không được khuyến khích.
class CoroutineTest {
@Test
fun `test suspend function`() = runBlocking {
val result = mySuspendFunction()
assertEquals("expected", result)
}
}
Đối với kiểm thử, nên sử dụng kotlinx-coroutines-test với TestCoroutineDispatcher thay vì runBlocking — điều này cung cấp khả năng kiểm soát thời gian và tránh chặn trong môi trường kiểm thử.
Việc chọn Coroutine Builder phụ thuộc vào kết quả trả về và kịch bản thực thi. Nếu thao tác không yêu cầu trả về dữ liệu — hãy sử dụng launch. Nếu bạn cần kết quả của một thao tác bất đồng bộ — hãy sử dụng async. Chỉ sử dụng runBlocking để làm cầu nối và thay thế produce bằng Flow cho các luồng phản ứng.
Trong các dự án Android sử dụng Kotlin Coroutines, cặp trình xây dựng chính là launch và async. launch được sử dụng trong ViewModel và UseCases để chạy coroutine, trong khi async được sử dụng cho các yêu cầu song song đến mạng hoặc cơ sở dữ liệu. Các thư viện hiện đại (Ktor, Room) đã hỗ trợ các hàm suspend, điều này giảm thiểu nhu cầu sử dụng async trực tiếp.
Câu hỏi thường gặp
launch trả về Job và không trả về kết quả thực thi, trong khi async trả về Deferred<T> — một đối tượng mà từ đó có thể lấy kết quả qua await(). launch được dùng cho các thao tác fire-and-forget, async dùng cho các tác vụ trả về dữ liệu.
Không được khuyến khích. runBlocking trên luồng chính gây ANR và chặn giao diện. Hãy sử dụng lifecycleScope.launch bên trong Activity và Fragment — đó là giải pháp tích hợp không chặn.
Trình xây dựng launch trả về một đối tượng Job, cho phép kiểm soát vòng đời coroutine: hủy (cancel), chờ hoàn thành (join), kiểm tra trạng thái (isActive, isCompleted, isCancelled).
Deferred<T> là một lời hứa bất đồng bộ về kết quả, được trả về bởi trình xây dựng async. Nó kế thừa từ Job và thêm các phương thức: await() để lấy kết quả, getCompleted() để truy cập không chặn và getCompletionExceptionOrNull() để kiểm tra ngoại lệ.
Sử dụng tham số CoroutineStart.LAZY: scope.launch(start = CoroutineStart.LAZY) { ... }. Sau đó gọi job.start() hoặc job.join() để thực thi thực tế. Điều này hữu ích cho khởi tạo trễ và thực thi coroutine có điều kiện.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm