lifecycleScope là một CoroutineScope tích hợp từ thư viện androidx.lifecycle, gắn với vòng đời của Activity, Fragment hoặc bất kỳ LifecycleOwner nào và tự động hủy các coroutine khi thành phần bị hủy. Theo Google Android Developers, 2025, lifecycleScope cho phép chạy các coroutine liên quan đến lớp UI một cách an toàn mà không có rủi ro thực thi mã sau khi Activity hoặc Fragment bị hủy. Scope tự động bị hủy khi LifecycleOwner chuyển sang trạng thái DESTROYED.
Những điểm chính
lifecycleScope là một thuộc tính mở rộng trên giao diện LifecycleOwner (Activity, Fragment, Service) cung cấp một CoroutineScope sẵn sàng, gắn với toàn bộ vòng đời của thành phần. Khi LifecycleOwner đạt đến trạng thái DESTROYED, lifecycleScope tự động hủy tất cả các coroutine đang hoạt động.
// In Fragment or Activity
lifecycleScope.launch {
delay(1000)
showSnackbar("Xin chào!")
}
Không giống viewModelScope, lifecycleScope bị hủy mỗi khi LifecycleOwner bị hủy — bao gồm cả xoay màn hình. Điều này làm cho nó trở nên lý tưởng cho các thao tác chỉ nên tồn tại khi một màn hình cụ thể đang hiển thị.
lifecycleScope khả dụng ở mọi nơi có LifecycleOwner:
Cơ chế hủy tự động của lifecycleScope dựa trên việc đăng ký các sự kiện Lifecycle. Khi Lifecycle giảm xuống dưới CREATED đến DESTROYED, scope bị hủy.
| Trạng thái | Mô tả | Scope hoạt động |
|---|---|---|
| CREATED | LifecycleOwner được tạo, onCreate đã thực thi | Có |
| STARTED | LifecycleOwner hiển thị (onStart) | Có |
| RESUMED | LifecycleOwner ở tiền cảnh (onResume) | Có |
| DESTROYED | LifecycleOwner bị hủy (onDestroy) | Không (scope đã hủy) |
lifecycleScope được tạo dưới dạng CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) và được lưu trữ bên trong Lifecycle. Khi Lifecycle chuyển sang trạng thái DESTROYED, scope.cancel() được gọi. Cơ chế này được thực hiện thông qua LifecycleEventObserver, đăng ký các sự kiện vòng đời khi truy cập scope lần đầu.
Khi màn hình được xoay, Activity bị hủy (onDestroy) và được tạo lại. lifecycleScope bị hủy cùng với Activity cũ và một thể hiện scope mới được tạo cho Activity mới. Đây là sự khác biệt cơ bản với viewModelScope, scope này tồn tại qua khi xoay màn hình.
Thư viện lifecycle cung cấp một số cách để khởi chạy coroutine thông qua lifecycleScope. Hãy xem sự phát triển của API từ các phương thức lỗi thời đến hiện đại.
Cách đơn giản nhất là lifecycleScope.launch { ... }. Coroutine bắt đầu ngay lập tức và bị hủy ở DESTROYED. Tuy nhiên, nó có thể thực thi mã ngay cả khi UI không hiển thị (ví dụ, ở nền sau onStop). Điều này không phải lúc nào cũng mong muốn.
Các phương thức này tạm dừng việc thực thi coroutine khi Lifecycle giảm xuống dưới trạng thái chỉ định và tiếp tục khi quay lại. Tuy nhiên, chúng đã bị đánh dấu @Deprecated trong lifecycle-runtime-ktx 2.6.0 vì:
repeatOnLifecycle là cách được Google khuyến nghị để khởi chạy các coroutine đồng bộ với vòng đời. Nó hủy và khởi động lại coroutine mỗi khi Lifecycle đạt đến trạng thái chỉ định.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state ->
updateUI(state)
}
}
}
Coroutine được truyền vào repeatOnLifecycle bắt đầu khi Lifecycle đạt đến STARTED và bị hủy khi nó giảm xuống dưới STARTED. Khi quay lại STARTED, coroutine khởi động lại từ đầu. Điều này an toàn và hiệu quả — không có coroutine nào bị tạm dừng.
Để thu thập dữ liệu từ Flow với nhận thức về vòng đời, có toán tử flowWithLifecycle. Nó tự động dừng và tiếp tục thu thập khi trạng thái Lifecycle thay đổi:
viewModel.uiState
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach { state -> updateUI(state) }
.launchIn(lifecycleScope)
Toán tử flowWithLifecycle là cách ngắn gọn nhất để đăng ký Flow một cách an toàn trong lớp UI.
Hãy xem ba kịch bản thực tế sử dụng lifecycleScope trong ứng dụng Android với Kotlin.
class MapFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
locationProvider.observeLocation().collect { loc ->
updateMapMarker(loc)
}
}
}
}
}
Coroutine bắt đầu khi fragment trở nên hiển thị (STARTED) và bị hủy khi nó rời màn hình (STOPPED). Nếu người dùng chuyển sang ứng dụng khác, các cập nhật vị trí không tiêu tốn pin.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
animateFadeIn(titleView)
delay(200)
animateSlideUp(contentView)
}
}
Hoạt ảnh chỉ chạy khi fragment ở tiền cảnh (RESUMED). Nếu người dùng thu nhỏ ứng dụng trong khi hoạt ảnh, coroutine bị hủy và khi quay lại, hoạt ảnh khởi động lại.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
while (isActive) {
syncData()
delay(30_000L)
}
}
}
Dữ liệu được đồng bộ 30 giây một lần, nhưng chỉ khi màn hình hiển thị. isActive kiểm tra xem coroutine đã bị hủy chưa, cung cấp lối thoát an toàn khỏi vòng lặp khi rời màn hình.
Cả hai scope đều gắn với vòng đời, nhưng ở các khía cạnh khác nhau của nó. Hiểu được sự khác biệt là rất quan trọng đối với kiến trúc ứng dụng Android đúng đắn.
viewModelScope gắn với ViewModel, scope này tồn tại qua khi xoay màn hình. lifecycleScope gắn với LifecycleOwner (Activity/Fragment), scope này bị hủy và được tạo lại khi xoay. Điều này quyết định các kịch bản sử dụng của chúng.
Trong thực tế, sự kết hợp cả hai scope là phổ biến: viewModelScope tải dữ liệu và quản lý trạng thái, trong khi lifecycleScope đăng ký Flow từ ViewModel với nhận thức về vòng đời. Sự phân tách trách nhiệm này được coi là thực hành tốt nhất trong phát triển Android hiện đại.
Hãy xem bốn lỗi phổ biến nhất mà các nhà phát triển mắc phải khi sử dụng lifecycleScope.
Nếu bạn khởi chạy tải dữ liệu trong lifecycleScope.launch, coroutine sẽ bị hủy khi xoay màn hình và dữ liệu sẽ phải tải lại. Sử dụng viewModelScope cho các thao tác lâu dài. lifecycleScope chỉ dành cho các tác vụ gắn với UI.
Gọi trực tiếp viewModel.someFlow.collect { ... } bên trong lifecycleScope.launch tiếp tục thu thập dữ liệu ngay cả khi màn hình không hiển thị. Điều này có thể dẫn đến cập nhật UI trong nền và chi phí không cần thiết. Luôn sử dụng repeatOnLifecycle hoặc flowWithLifecycle.
Mặc dù lifecycleScope bị hủy ở DESTROYED, mã sau điểm tạm dừng (suspend) có thể không thực thi khi bị hủy đột ngột. Đừng dựa vào việc thực thi mã sau cuộc gọi suspend trừ khi bạn sử dụng NonCancellable.
launchWhenStarted và các phương thức tương tự không hủy coroutine, chỉ tạm dừng. Nếu màn hình chuyển đổi giữa tiền cảnh và nền nhiều lần, coroutine sẽ tích tụ các lời gọi bị hoãn. Chuyển sang repeatOnLifecycle — đây là cách duy nhất để đồng bộ với Lifecycle.
Câu hỏi thường gặp
lifecycleScope tự động bị hủy khi LifecycleOwner bị hủy. GlobalScope tồn tại trong suốt thời gian chạy của ứng dụng. Coroutine trong lifecycleScope không thể cập nhật UI sau khi thành phần bị hủy, trong khi GlobalScope có thể, dẫn đến sự cố. Luôn sử dụng lifecycleScope trong lớp UI.
Không, ViewModel không phải là LifecycleOwner, vì vậy lifecycleScope không khả dụng trong đó. ViewModel sử dụng viewModelScope. Nếu mã cần chạy trong cả hai ngữ cảnh, hãy trích xuất logic vào use case hoặc repository với các hàm suspend.
Mỗi lần gọi repeatOnLifecycle tạo một coroutine mới chạy khối khi đạt đến trạng thái Lifecycle được chỉ định. Nếu repeatOnLifecycle được gọi hai lần cho cùng một trạng thái, cả hai khối sẽ chạy độc lập. Thông thường, một lần gọi trong onViewCreated là đủ.
Bạn không thể trực tiếp thay đổi bộ điều phối của lifecycleScope — nó sử dụng Dispatchers.Main.immediate. Bên trong khối coroutine, bạn có thể chuyển sang bộ điều phối khác thông qua withContext. Đối với kiểm thử, hãy sử dụng TestDispatcher với LifecycleOwner.
lifecycleScope bị hủy khi LifecycleOwner chuyển sang trạng thái DESTROYED (sau onDestroy). Các lời gọi lifecycleScope.launch đơn giản không bị hủy trong onPause hoặc onStop. Để tạm dừng khi chuyển sang nền, hãy sử dụng repeatOnLifecycle(STARTED) hoặc repeatOnLifecycle(RESUMED).
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