launch — một Coroutine Builder trong Kotlin khởi chạy một coroutine mà không trả về kết quả, trả về một đối tượng Job để điều khiển thực thi. Builder này triển khai mẫu fire-and-forget: coroutine bắt đầu làm việc trong luồng nền và tự động hoàn thành. Theo tài liệu JetBrains, 2024, launch là cách chính để chạy các tác vụ song song trong Kotlin Coroutines.
Những điểm chính
launch là một hàm mở rộng của Kotlin, có sẵn qua import kotlinx.coroutines, tạo một coroutine mới trong CoroutineScope nhất định. Không giống các hàm thông thường, mã bên trong launch thực thi đồng thời — coroutine có thể tạm dừng (suspend) mà không chặn luồng và tiếp tục sau đó.
Builder launch là khối xây dựng cơ bản của mô hình coroutine Kotlin. Nó không trả về giá trị tính toán nhưng trả về một đối tượng Job cho phép điều khiển thực thi coroutine. Tất cả coroutine con được khởi chạy qua launch bên trong một coroutine khác đều được gắn với coroutine cha.
Builder launch được định nghĩa như một hàm inline với nhiều tham số: CoroutineScope, CoroutineContext, CoroutineStart và một khối suspend. Các tham số có giá trị mặc định, giúp launch thuận tiện cho các tình huống điển hình.
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
Tham số context chấp nhận một CoroutineContext kết hợp dispatcher, trình xử lý ngoại lệ và phần tử có tên. Thông thường nhất, Dispatchers.IO, Dispatchers.Main hoặc SupervisorJob được truyền để cách ly lỗi.
Job — đối tượng được trả về đại diện cho vòng đời của coroutine. Job có thể ở các trạng thái: New, Active, Completing, Completed, Cancelling, Cancelled. Mỗi trạng thái phản ánh trạng thái thực thi và cho phép phản ứng với các thay đổi.
val job = CoroutineScope(Dispatchers.IO).launch {
repeat(10) { i ->
delay(1000L)
println("Tiến trình: $i")
}
}
println("Job đang hoạt động: ${job.isActive}")
delay(2500L)
job.cancel()
println("Job đã bị hủy: ${job.isCancelled}")
Job hỗ trợ phân cấp: nếu coroutine cha bị hủy, tất cả coroutine con tự động bị hủy. SupervisorJob thay đổi hành vi này — coroutine con không bị hủy khi một trong chúng gặp lỗi.
Tính đồng thời có cấu trúc có nghĩa là mỗi coroutine được khởi chạy trong một CoroutineScope cụ thể và scope sẽ không hoàn thành cho đến khi tất cả coroutine con kết thúc công việc. launch hoàn toàn hỗ trợ nguyên tắc này — coroutine được khởi chạy bên trong một coroutine khác là coroutine con của nó.
Với các lời gọi launch lồng nhau, một cây coroutine được hình thành. Coroutine cha chờ tất cả coroutine con hoàn thành, đảm bảo thứ tự thực thi có thể dự đoán và đơn giản hóa quản lý tài nguyên.
fun main() = runBlocking {
launch {
launch {
delay(1000L)
println("Con 1 hoàn thành")
}
launch {
delay(500L)
println("Con 2 hoàn thành")
}
println("Tất cả con hoàn thành trước dòng này")
}
}
Hành vi này khác với tính đồng thời dựa trên luồng, nơi các luồng con không gắn với luồng cha. Trong coroutine, coroutine cha không hoàn thành cho đến khi tất cả coroutine con kết thúc công việc.
Xử lý ngoại lệ trong launch phụ thuộc vào loại Job. Đối với Job thông thường, ngoại lệ lan truyền đến coroutine cha và hủy nó. Đối với SupervisorJob hoặc SupervisorScope, ngoại lệ được cách ly — lỗi trong một coroutine con không ảnh hưởng đến các coroutine khác.
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
try {
riskyOperation()
} catch (e: Exception) {
println("Đã bắt: ${e.message}")
}
}
scope.launch {
println("Việc này vẫn chạy nhờ SupervisorJob")
}
Để xử lý toàn cục các ngoại lệ không bắt được, CoroutineExceptionHandler được sử dụng. Nó bắt các ngoại lệ không được xử lý bên trong coroutine và cho phép ghi lỗi mà không làm ứng dụng bị treo.
Trong Android, launch được sử dụng ở khắp mọi nơi: từ ViewModel đến WorkManager. Mẫu chính là sử dụng lifecycleScope trong Fragment và viewModelScope trong ViewModel để tự động hủy coroutine khi vòng đời thành phần kết thúc.
class ProfileViewModel : ViewModel() {
private val repository = UserRepository()
fun loadProfile(userId: String) {
viewModelScope.launch(Dispatchers.IO) {
val profile = repository.fetchProfile(userId)
withContext(Dispatchers.Main) {
_profileState.update { it.copy(profile = profile) }
}
}
}
}
viewModelScope tự động hủy coroutine khi ViewModel bị phá hủy. Điều này loại bỏ rò rỉ và đảm bảo các hoạt động nền không tiếp tục sau khi mất ngữ cảnh. Ngoài ra, withContext có thể được sử dụng để chuyển đổi giữa các dispatcher.
Câu hỏi thường gặp
launch trả về Job và không trả về kết quả, trong khi async trả về Deferred<T> để lấy kết quả. launch được dùng cho các thao tác fire-and-forget, async — khi bạn cần đợi và sử dụng giá trị được trả về.
Gọi job.cancel() trên đối tượng Job đã lưu. Để hủy nhóm, hủy toàn bộ CoroutineScope qua scope.cancel(). Coroutine phải hợp tác — kiểm tra isActive hoặc sử dụng các hàm suspend có thể hủy.
Sử dụng try-catch bên trong khối launch hoặc truyền CoroutineExceptionHandler trong CoroutineContext. Để cách ly lỗi, sử dụng SupervisorJob — khi đó ngoại lệ trong một coroutine con sẽ không hủy các coroutine khác.
Không, launch là một hàm mở rộng của CoroutineScope. Không có scope, coroutine không thể tuân theo nguyên tắc đồng thời có cấu trúc. Sử dụng GlobalScope.launch một cách thận trọng — nó tạo ra một coroutine không gắn với vòng đời.
Số lượng bị giới hạn bởi nhóm luồng của dispatcher. Dispatchers.Default sử dụng số luồng bằng số lõi CPU. Dispatchers.IO hỗ trợ tới 64 luồng. Nếu khởi chạy nhiều coroutine hơn, chúng sẽ được xếp hàng đợi.
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