lifecycleScope — nó là gì, liên kết với Lifecycle và làm việc trong Android

Tác giả: IT Sectr Đã đăng: 2026-06-23 Thời gian đọc: 9 phút

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 — CoroutineScope từ lifecycle-runtime-ktx, bị hủy ở trạng thái DESTROYED của Lifecycle
  • lifecycleScope.launch — khởi chạy coroutine tự động bị hủy khi LifecycleOwner bị hủy
  • launchWhenStarted / launchWhenResumed — phương thức lỗi thời, đã được thay thế bằng repeatOnLifecycle
  • repeatOnLifecycle — API hiện đại để khởi chạy coroutine theo một trạng thái Lifecycle cụ thể
  • Dispatchers.Main.immediate — bộ điều phối mặc định cho lifecycleScope

lifecycleScope trong Android là gì?

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.

kotlin
// 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ị.

Nơi lifecycleScope khả dụng

lifecycleScope khả dụng ở mọi nơi có LifecycleOwner:

  • Activity — AppCompatActivity kế thừa LifecycleOwner
  • Fragment — Fragment kế thừa LifecycleOwner
  • LifecycleService — dịch vụ có vòng đời
  • ProcessLifecycleOwner — vòng đời của toàn bộ ứng dụng
  • LifecycleOwner tùy chỉnh — bất kỳ đối tượng nào triển khai LifecycleOwner

lifecycleScope hoạt động như thế nào: Lifecycle và hủy tự động

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.

Các trạng thái Lifecycle

Trạng tháiMô tảScope hoạt động
CREATEDLifecycleOwner được tạo, onCreate đã thực thi
STARTEDLifecycleOwner hiển thị (onStart)
RESUMEDLifecycleOwner ở tiền cảnh (onResume)
DESTROYEDLifecycleOwner bị hủy (onDestroy)Không (scope đã hủy)

Cấu trúc bên trong của lifecycleScope

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.

Hành vi khi xoay

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.

API lifecycleScope: launch, launchWhen và repeatOnLifecycle

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.

lifecycleScope.launch — Khởi chạy cơ bản

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.

Lỗi thời: launchWhenCreated / launchWhenStarted / launchWhenResumed

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ì:

  • Chúng không hủy coroutine — chỉ tạm dừng
  • Chúng dẫn đến tích tụ các coroutine bị tạm dừng, tiêu tốn bộ nhớ
  • Chúng tạo ra các điều kiện cạnh tranh khi chuyển đổi trạng thái nhanh

API hiện đại: repeatOnLifecycle

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.

kotlin
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.

flowWithLifecycle — Dành cho Flow

Để 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:

kotlin
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.

Ví dụ sử dụng lifecycleScope

Hãy xem ba kịch bản thực tế sử dụng lifecycleScope trong ứng dụng Android với Kotlin.

Ví dụ 1: Đăng ký cập nhật vị trí chỉ khi màn hình hiển thị

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.

Ví dụ 2: Chạy hoạt ảnh khi fragment bắt đầu

kotlin
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.

Ví dụ 3: Đồng bộ dữ liệu định kỳ trên màn hình hiển thị

kotlin
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.

lifecycleScope so với viewModelScope: các kịch bản sử dụng

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.

Sự khác biệt chính

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.

Khi nào sử dụng lifecycleScope

  • Đăng ký các sự kiện hệ thống (vị trí, cảm biến, camera)
  • Hoạt ảnh và hiệu ứng UI gắn với một màn hình cụ thể
  • Tải dữ liệu theo khối dựa trên khả năng hiển thị của màn hình
  • Các thao tác sẽ dừng lại khi rời màn hình

Khi nào sử dụng viewModelScope

  • Tải dữ liệu từ Repository
  • Logic nghiệp vụ cần tồn tại qua khi xoay
  • Lưu cache và xử lý dữ liệu
  • Bất kỳ thao tác nào mà kết quả cần sau khi xoay

Sử dụng kết hợp

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.

Lỗi thường gặp khi làm việc với lifecycleScope

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.

Lỗi 1: Sử dụng lifecycleScope thay vì viewModelScope để tải dữ liệu

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.

Lỗi 2: Thu thập Flow mà không có repeatOnLifecycle

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.

Lỗi 3: Quên hủy khi rời màn hình

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.

Lỗi 4: Sử dụng launchWhenStarted lỗi thời thay vì repeatOnLifecycle

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

Sự khác biệt giữa lifecycleScope và GlobalScope là gì?

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.

Có thể sử dụng lifecycleScope trong ViewModel không?

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.

Điều gì xảy ra khi repeatOnLifecycle được gọi nhiều lần?

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à đủ.

Có thể đặt Dispatcher tùy chỉnh cho lifecycleScope không?

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 ở onPause hay onDestroy?

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

  • lifecycleScope — CoroutineScope gắn với LifecycleOwner và tự động bị hủy ở DESTROYED thông qua LifecycleEventObserver
  • Dispatchers.Main.immediate — bộ điều phối mặc định, đảm bảo cập nhật UI an toàn mà không cần chuyển đổi không cần thiết
  • repeatOnLifecycle — API hiện đại để khởi chạy coroutine ở trạng thái Lifecycle chỉ định với tự động hủy và khởi động lại
  • flowWithLifecycle — toán tử thu thập Flow an toàn từ UI với nhận thức về vòng đời
  • launchWhenStarted đã lỗi thời — thay vì tạm dừng, hãy sử dụng repeatOnLifecycle, scope này hủy coroutine thay vì tạm dừng
  • lifecycleScope so với viewModelScope — lifecycleScope cho thao tác UI (hoạt ảnh, vị trí), viewModelScope cho dữ liệu và logic nghiệp vụ
  • Xoay màn hình — lifecycleScope bị hủy khi xoay, viewModelScope tồn tại; chọn scope dựa theo nhiệm vụ

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.

Thảo luận dự án

Đọc thêm