Chu kỳ hoạt động

Khi người dùng di chuyển qua, rời khỏi và quay lại ứng dụng của bạn, các thực thể Activity trong ứng dụng đó sẽ chuyển đổi qua nhiều trạng thái trong vòng đời của chúng. Lớp Activity cung cấp một số lệnh gọi lại cho phép hoạt động biết khi nào trạng thái thay đổi hoặc hệ thống đang tạo, dừng, tiếp tục một hoạt động hoặc huỷ quy trình mà hoạt động đó nằm trong.

Trong các phương thức gọi lại trong vòng đời, bạn có thể khai báo cách hoạt động của mình hoạt động khi người dùng rời khỏi và truy cập lại vào hoạt động. Ví dụ: nếu đang tạo một trình phát video trực tuyến, bạn có thể tạm dừng video và chấm dứt kết nối mạng khi người dùng chuyển sang một ứng dụng khác. Khi người dùng quay lại, bạn có thể kết nối lại với mạng và cho phép người dùng tiếp tục xem video từ cùng một vị trí.

Mỗi lệnh gọi lại cho phép bạn thực hiện công việc cụ thể phù hợp với một thay đổi trạng thái nhất định. Thực hiện đúng công việc vào đúng thời điểm và xử lý các quá trình chuyển đổi đúng cách sẽ giúp ứng dụng của bạn mạnh mẽ và hiệu quả hơn. Ví dụ: việc triển khai tốt các lệnh gọi lại vòng đời có thể giúp ứng dụng của bạn tránh được những vấn đề sau:

  • Gặp sự cố nếu người dùng nhận được cuộc gọi điện thoại hoặc chuyển sang một ứng dụng khác trong khi đang dùng ứng dụng của bạn.
  • Tiêu thụ tài nguyên hệ thống có giá trị khi người dùng không sử dụng ứng dụng một cách tích cực.
  • Mất tiến trình của người dùng nếu họ rời khỏi ứng dụng của bạn và quay lại vào lúc khác.
  • Gặp sự cố hoặc mất tiến trình của người dùng khi màn hình xoay giữa hướng ngang và hướng dọc.

Tài liệu này giải thích chi tiết về vòng đời của activity. Tài liệu này bắt đầu bằng cách mô tả mô hình vòng đời. Tiếp theo, phần này giải thích từng lệnh gọi lại: những gì xảy ra nội bộ trong khi các lệnh gọi lại thực thi và những gì bạn cần triển khai trong các lệnh gọi lại đó.

Sau đó, phần này giới thiệu ngắn gọn về mối quan hệ giữa trạng thái của activity và khả năng một quy trình bị hệ thống huỷ. Cuối cùng, phần này thảo luận về một số chủ đề liên quan đến quá trình chuyển đổi giữa các trạng thái hoạt động.

Để biết thông tin về cách xử lý vòng đời, bao gồm cả hướng dẫn về các phương pháp hay nhất, hãy xem bài viết Vòng đời trong Jetpack ComposeLưu trạng thái giao diện người dùng. Để tìm hiểu cách xây dựng một ứng dụng mạnh mẽ, đủ tiêu chuẩn phát hành công khai bằng cách sử dụng các hoạt động kết hợp với các thành phần kiến trúc, hãy xem Hướng dẫn về cấu trúc ứng dụng.

Các khái niệm về vòng đời hoạt động

Để điều hướng các quá trình chuyển đổi giữa các giai đoạn của vòng đời của activity, lớp Activity cung cấp một tập hợp cốt lõi gồm 6 lệnh gọi lại: onCreate, onStart, onResume, onPause, onStoponDestroy. Hệ thống sẽ gọi từng lệnh gọi lại này khi hoạt động chuyển sang một trạng thái mới.

Hình 1 trình bày một hình ảnh minh hoạ về mô hình này.

Hình 1. Hình minh hoạ đơn giản về vòng đời của activity.

Khi người dùng bắt đầu rời khỏi hoạt động, hệ thống sẽ gọi các phương thức để tháo dỡ hoạt động. Trong một số trường hợp, hoạt động chỉ bị tháo dỡ một phần và vẫn nằm trong bộ nhớ, chẳng hạn như khi người dùng chuyển sang một ứng dụng khác. Trong những trường hợp này, hoạt động vẫn có thể quay lại nền trước.

Nếu người dùng quay lại hoạt động, hoạt động đó sẽ tiếp tục từ nơi người dùng rời đi. Với một số trường hợp ngoại lệ, các ứng dụng bị hạn chế bắt đầu hoạt động khi chạy ở chế độ nền.

Khả năng hệ thống sẽ huỷ một quy trình nhất định, cùng với các hoạt động trong quy trình đó, phụ thuộc vào trạng thái của hoạt động tại thời điểm đó. Để biết thêm thông tin về mối quan hệ giữa trạng thái của activity và khả năng bị loại bỏ, hãy xem phần về trạng thái của activity và việc loại bỏ khỏi bộ nhớ.

Tuỳ thuộc vào độ phức tạp của hoạt động, có thể bạn không cần triển khai tất cả các phương thức vòng đời. Tuy nhiên, bạn cần hiểu rõ từng loại và triển khai những loại khiến ứng dụng của bạn hoạt động theo cách mà người dùng mong đợi.

Compose và Lifecycle

Trong Compose, hãy tránh đặt trực tiếp logic nghiệp vụ hoặc thiết lập trình theo dõi theo cách thủ công trong các lệnh gọi lại hoạt động như onStart hoặc onResume. Thay vào đó, hãy sử dụng các hiệu ứng nhận biết Vòng đời và các đối tượng tiếp nhận dữ liệu nhận biết trạng thái sẽ tự động điều chỉnh theo sự hiện diện của giao diện người dùng trên màn hình.

  • Thu thập dữ liệu nhận biết vòng đời: Sử dụng collectAsStateWithLifecycle để sử dụng các luồng từ ViewModel. API này tự động bắt đầu thu thập khi giao diện người dùng chuyển sang trạng thái Đã bắt đầu và dừng khi chuyển sang chế độ nền, ngăn chặn tình trạng tiêu thụ tài nguyên không cần thiết. Sau khi thu thập luồng dưới dạng trạng thái, bạn có thể dùng LifecycleEffects để chạy mã khi một sự kiện trong vòng đời xảy ra.
  • Luồng logic: Bằng cách sử dụng các API này, giao diện người dùng sẽ phản ứng với trạng thái vòng đời một cách tự nhiên thông qua cây thành phần, đảm bảo rằng logic nghiệp vụ chỉ thực thi khi người dùng đang tích cực tương tác với thành phần.

Để biết thêm về Compose và vòng đời, hãy xem bài viết Vòng đời trong Jetpack Compose.

Phương thức gọi lại trong vòng đời

Phần này cung cấp thông tin về khái niệm và cách triển khai đối với các phương thức gọi lại được dùng trong vòng đời của activity.

Một số thao tác thuộc về các phương thức vòng đời của activity. Tuy nhiên, hãy đặt mã triển khai các hành động của một thành phần phụ thuộc trong thành phần, thay vì phương thức vòng đời của activity. Để đạt được điều này, bạn cần phải làm cho thành phần phụ thuộc nhận biết được vòng đời. Để tìm hiểu cách giúp các thành phần phụ thuộc nhận biết được vòng đời, hãy xem bài viết Vòng đời trong Jetpack Compose.

onCreate

Bạn phải triển khai lệnh gọi lại này. Lệnh gọi lại này sẽ kích hoạt khi hệ thống tạo hoạt động lần đầu tiên. Khi được tạo, hoạt động sẽ chuyển sang trạng thái Đã tạo. Trong phương thức onCreate, hãy thực hiện logic khởi động ứng dụng cơ bản chỉ xảy ra một lần trong toàn bộ vòng đời của hoạt động.

Ví dụ: việc triển khai onCreate có thể liên kết dữ liệu với danh sách, liên kết hoạt động với ViewModel và khởi tạo một số biến phạm vi lớp. Phương thức này nhận tham số savedInstanceState, là một đối tượng Bundle chứa trạng thái đã lưu trước đó của hoạt động. Nếu hoạt động chưa từng tồn tại trước đây, thì giá trị của đối tượng Bundle sẽ là giá trị rỗng.

Nếu bạn có một thành phần nhận biết vòng đời được kết nối với vòng đời của hoạt động, thì thành phần đó sẽ nhận được sự kiện ON_CREATE. Phương thức được chú thích bằng @OnLifecycleEvent sẽ được gọi để thành phần nhận biết vòng đời của bạn có thể thực hiện mọi mã thiết lập cần thiết cho trạng thái đã tạo.

Ví dụ sau đây cho thấy cách tích hợp một thành phần kết hợp Text trong một hoạt động tối thiểu:

class ExampleActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent { // In here, we can call composables!
            MaterialTheme {
                Greeting(name = "compose")
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

Hoạt động của bạn không duy trì ở trạng thái Đã tạo. Sau khi phương thức onCreate hoàn tất quá trình thực thi, hoạt động sẽ chuyển sang trạng thái Đã bắt đầu và hệ thống sẽ gọi các phương thức onStartonResume một cách nhanh chóng.

onStart

Khi hoạt động chuyển sang trạng thái Đã bắt đầu, hệ thống sẽ gọi onStart. Lệnh gọi này giúp người dùng nhìn thấy hoạt động khi ứng dụng chuẩn bị cho hoạt động chuyển sang nền trước và trở nên có thể tương tác. Ví dụ: phương thức này là nơi khởi chạy mã duy trì giao diện người dùng.

Khi hoạt động chuyển sang trạng thái Bắt đầu, mọi thành phần có nhận biết vòng đời được liên kết với vòng đời của hoạt động sẽ nhận được sự kiện ON_START.

Phương thức onStart hoàn tất nhanh chóng và cũng như trạng thái Đã tạo, hoạt động không duy trì ở trạng thái Đã bắt đầu. Sau khi lệnh gọi lại này hoàn tất, hoạt động sẽ chuyển sang trạng thái Đã tiếp tục và hệ thống sẽ gọi phương thức onResume.

onResume

Khi hoạt động chuyển sang trạng thái Đã tiếp tục, hoạt động sẽ chuyển sang nền trước và hệ thống sẽ gọi lệnh gọi lại onResume. Đây là trạng thái mà ứng dụng tương tác với người dùng. Ứng dụng vẫn ở trạng thái này cho đến khi có điều gì đó xảy ra khiến ứng dụng mất tiêu điểm, chẳng hạn như thiết bị nhận được cuộc gọi điện thoại, người dùng chuyển đến một hoạt động khác hoặc màn hình thiết bị tắt.

Khi hoạt động chuyển sang trạng thái Tiếp tục, mọi thành phần có nhận biết vòng đời được liên kết với vòng đời của hoạt động sẽ nhận được sự kiện ON_RESUME. Đây là nơi các thành phần vòng đời có thể bật mọi chức năng cần chạy trong khi thành phần hiển thị và ở nền trước, chẳng hạn như bắt đầu xem trước camera.

Khi một sự kiện gây gián đoạn xảy ra, hoạt động sẽ chuyển sang trạng thái Tạm dừng và hệ thống sẽ gọi lệnh gọi lại onPause.

Nếu hoạt động chuyển từ trạng thái Paused (Tạm dừng) sang trạng thái Resumed (Đã tiếp tục), hệ thống sẽ gọi lại phương thức onResume. Vì lý do này, hãy triển khai onResume để khởi chạy các thành phần mà bạn phát hành trong onPause và thực hiện mọi thao tác khởi chạy khác phải diễn ra mỗi khi hoạt động chuyển sang trạng thái Đã tiếp tục.

Dưới đây là ví dụ về một thành phần nhận biết vòng đời truy cập vào camera khi thành phần nhận được sự kiện ON_RESUME:

class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun initializeCamera() {
        if (camera == null) {
            getCamera()
        }
    }
    ...
}

Đoạn mã trước đó sẽ khởi chạy camera sau khi LifecycleObserver nhận được sự kiện ON_RESUME. Tuy nhiên, ở chế độ nhiều cửa sổ, hoạt động của bạn có thể hiển thị đầy đủ ngay cả khi ở trạng thái Tạm dừng. Ví dụ: khi ứng dụng ở chế độ nhiều cửa sổ và người dùng nhấn vào cửa sổ không chứa hoạt động của bạn, hoạt động đó sẽ chuyển sang trạng thái Tạm dừng.

Nếu bạn chỉ muốn máy ảnh hoạt động khi ứng dụng ở trạng thái Đã tiếp tục (hiển thị và hoạt động ở nền trước), hãy khởi chạy máy ảnh sau sự kiện ON_RESUME như minh hoạ trước đó. Nếu muốn duy trì trạng thái hoạt động của camera trong khi hoạt động ở trạng thái Tạm dừng nhưng vẫn hiển thị (chẳng hạn như ở chế độ nhiều cửa sổ), hãy khởi chạy camera sau sự kiện ON_START.

Tuy nhiên, việc kích hoạt camera trong khi hoạt động của bạn ở trạng thái Tạm dừng có thể từ chối quyền truy cập vào camera đối với một ứng dụng khác ở trạng thái Tiếp tục trong chế độ nhiều cửa sổ. Đôi khi, bạn cần duy trì trạng thái hoạt động của camera trong khi hoạt động của bạn đang ở trạng thái Tạm dừng, nhưng điều này có thể làm giảm trải nghiệm tổng thể của người dùng.

Vì lý do này, hãy cân nhắc kỹ xem thời điểm nào trong vòng đời là phù hợp nhất để kiểm soát các tài nguyên hệ thống dùng chung trong bối cảnh chế độ nhiều cửa sổ. Để tìm hiểu thêm về cách hỗ trợ chế độ nhiều cửa sổ, hãy xem bài viết Hỗ trợ chế độ nhiều cửa sổ.

Bất kể bạn chọn thực hiện thao tác khởi tạo trong sự kiện tạo nào, hãy nhớ sử dụng sự kiện trong vòng đời tương ứng để phát hành tài nguyên. Nếu bạn khởi tạo một thứ gì đó sau sự kiện ON_START, hãy phát hành hoặc chấm dứt sự kiện đó sau sự kiện ON_STOP. Nếu bạn khởi chạy sau sự kiện ON_RESUME, hãy phát hành sau sự kiện ON_PAUSE.

Đoạn mã trước đó đặt mã khởi tạo camera trong một thành phần nhận biết vòng đời. Thay vào đó, bạn có thể đặt mã này trực tiếp vào các phương thức gọi lại trong vòng đời của activity, chẳng hạn như onStartonStop, nhưng chúng tôi không khuyến khích bạn làm như vậy. Việc thêm logic này vào một thành phần độc lập, nhận biết vòng đời cho phép bạn dùng lại thành phần này trên nhiều hoạt động mà không cần phải sao chép mã. Để tìm hiểu cách tạo một thành phần nhận biết vòng đời, hãy xem phần Vòng đời trong Jetpack Compose.

onPause

Hệ thống gọi phương thức này làm chỉ báo đầu tiên cho biết người dùng đang rời khỏi hoạt động của bạn, mặc dù không phải lúc nào điều này cũng có nghĩa là hoạt động đang bị huỷ. Điều này cho biết hoạt động không còn ở nền trước nữa, nhưng vẫn hiển thị nếu người dùng đang ở chế độ nhiều cửa sổ. Có một số lý do khiến một hoạt động có thể chuyển sang trạng thái này:

  • Một sự kiện làm gián đoạn quá trình thực thi ứng dụng, như mô tả trong phần về lệnh gọi lại onResume, sẽ tạm dừng hoạt động hiện tại. Đây là trường hợp phổ biến nhất.
  • Ở chế độ nhiều cửa sổ, chỉ có một ứng dụng được lấy tiêu điểm tại một thời điểm bất kỳ và hệ thống sẽ tạm dừng tất cả các ứng dụng khác.
  • Việc mở một hoạt động mới, bán trong suốt (chẳng hạn như hộp thoại) sẽ tạm dừng hoạt động mà hoạt động đó che phủ. Miễn là hoạt động hiển thị một phần nhưng không được lấy tiêu điểm, hoạt động đó vẫn ở trạng thái tạm dừng.

Khi một hoạt động chuyển sang trạng thái Tạm dừng, mọi thành phần biết vòng đời được liên kết với vòng đời của hoạt động đó sẽ nhận được sự kiện ON_PAUSE. Đây là nơi các thành phần vòng đời có thể dừng mọi chức năng không cần chạy khi thành phần không ở nền trước, chẳng hạn như dừng bản xem trước của camera.

Sử dụng phương thức onPause để tạm dừng hoặc điều chỉnh các thao tác không thể tiếp tục hoặc có thể tiếp tục ở mức độ vừa phải, trong khi Activity ở trạng thái Paused (Đã tạm dừng) và bạn dự kiến sẽ tiếp tục trong thời gian ngắn.

Bạn cũng có thể dùng phương thức onPause để giải phóng tài nguyên hệ thống, các thao tác đối với cảm biến (chẳng hạn như GPS) hoặc mọi tài nguyên ảnh hưởng đến thời lượng pin trong khi hoạt động của bạn ở trạng thái Tạm dừng và người dùng không cần đến những tài nguyên đó.

Tuy nhiên, như đã đề cập trong phần về onResume, một hoạt động bị Tạm dừng vẫn có thể hiển thị đầy đủ nếu ứng dụng ở chế độ nhiều cửa sổ. Hãy cân nhắc sử dụng onStop thay vì onPause để phát hành hoàn toàn hoặc điều chỉnh các tài nguyên và thao tác liên quan đến giao diện người dùng nhằm hỗ trợ tốt hơn chế độ nhiều cửa sổ.

Ví dụ sau đây về LifecycleObserver phản ứng với sự kiện ON_PAUSE là đối tượng tương ứng với ví dụ về sự kiện ON_RESUME trước đó, giải phóng máy ảnh khởi động sau khi nhận được sự kiện ON_RESUME:

class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun releaseCamera() {
        camera?.release()
        camera = null
    }
    ...
}

Ví dụ này đặt mã phát hành camera sau khi LifecycleObserver nhận được sự kiện ON_PAUSE.

Việc thực thi onPause diễn ra rất nhanh và không nhất thiết cung cấp đủ thời gian để thực hiện các thao tác lưu. Vì lý do này, đừng dùng onPause để lưu dữ liệu ứng dụng hoặc dữ liệu người dùng, thực hiện các lệnh gọi mạng hoặc thực thi giao dịch cơ sở dữ liệu. Công việc như vậy có thể không hoàn tất trước khi phương thức hoàn tất.

Thay vào đó, hãy thực hiện các thao tác tắt khi tải nặng trong onStop. Để biết thêm thông tin về các thao tác phù hợp cần thực hiện trong onStop, hãy xem phần tiếp theo. Để biết thêm thông tin về cách lưu dữ liệu, hãy xem phần lưu và khôi phục trạng thái.

Việc hoàn tất phương thức onPause không có nghĩa là hoạt động sẽ rời khỏi trạng thái Tạm dừng. Thay vào đó, hoạt động vẫn ở trạng thái này cho đến khi hoạt động tiếp tục hoặc hoàn toàn không hiển thị với người dùng. Nếu hoạt động tiếp tục, hệ thống sẽ gọi lại lệnh gọi lại onResume.

Nếu hoạt động quay lại từ trạng thái Tạm dừng sang trạng thái Đã tiếp tục, hệ thống sẽ giữ phiên bản Activity thường trú trong bộ nhớ, gọi lại phiên bản đó khi hệ thống gọi onResume. Trong trường hợp này, bạn không cần khởi động lại các thành phần được tạo trong bất kỳ phương thức gọi lại nào dẫn đến trạng thái Resumed. Nếu hoạt động hoàn toàn không hiển thị, hệ thống sẽ gọi onStop.

onStop

Khi hoạt động không còn hiển thị với người dùng, hoạt động đó sẽ chuyển sang trạng thái Đã dừng và hệ thống sẽ gọi lệnh gọi lại onStop. Điều này có thể xảy ra khi một hoạt động mới ra mắt bao phủ toàn bộ màn hình. Hệ thống cũng gọi onStop khi hoạt động kết thúc và sắp bị chấm dứt.

Khi hoạt động chuyển sang trạng thái Dừng, mọi thành phần có nhận biết vòng đời được liên kết với vòng đời của hoạt động sẽ nhận được sự kiện ON_STOP. Đây là nơi các thành phần vòng đời có thể dừng mọi chức năng không cần chạy trong khi thành phần không xuất hiện trên màn hình.

Trong phương thức onStop, hãy giải phóng hoặc điều chỉnh những tài nguyên không cần thiết khi người dùng không thấy ứng dụng. Ví dụ: ứng dụng của bạn có thể tạm dừng ảnh động hoặc chuyển từ cập nhật vị trí chi tiết sang cập nhật vị trí tương đối. Việc sử dụng onStop thay vì onPause có nghĩa là công việc liên quan đến giao diện người dùng vẫn tiếp tục, ngay cả khi người dùng đang xem hoạt động của bạn ở chế độ nhiều cửa sổ.

Ngoài ra, hãy dùng onStop để thực hiện các thao tác tắt tương đối tốn nhiều CPU. Ví dụ: nếu không tìm được thời điểm thích hợp hơn để lưu thông tin vào cơ sở dữ liệu, bạn có thể thực hiện việc này trong onStop. Ví dụ sau đây minh hoạ cách triển khai onStop để lưu nội dung của một bản nháp ghi chú vào bộ nhớ liên tục:

override fun onStop() {
    super.onStop()

    // Delegate the save operation to the ViewModel, which handles the
    // background thread operations (e.g., using Kotlin Coroutines and Room).
    noteViewModel.saveDraft()
}

Khi hoạt động chuyển sang trạng thái Đã dừng, đối tượng Activity sẽ được lưu giữ trong bộ nhớ: đối tượng này duy trì tất cả thông tin về trạng thái và thành viên, nhưng không được đính kèm vào trình quản lý cửa sổ. Khi hoạt động tiếp tục, hoạt động này sẽ gọi lại thông tin này.

Từ trạng thái Đã dừng, hoạt động sẽ quay lại để tương tác với người dùng hoặc hoạt động đã chạy xong và biến mất. Nếu hoạt động quay lại, hệ thống sẽ gọi onRestart. Nếu Activity đã chạy xong, hệ thống sẽ gọi onDestroy.

onDestroy

onDestroy được gọi trước khi hoạt động bị huỷ. Hệ thống gọi lệnh gọi lại này vì một trong hai lý do:

  1. Hoạt động đang kết thúc, do người dùng đóng hoàn toàn hoạt động hoặc do finish được gọi trên hoạt động.
  2. Hệ thống tạm thời huỷ hoạt động do thay đổi cấu hình, chẳng hạn như xoay thiết bị hoặc chuyển sang chế độ nhiều cửa sổ.

Khi hoạt động chuyển sang trạng thái bị huỷ, mọi thành phần có nhận biết vòng đời được liên kết với vòng đời của hoạt động sẽ nhận được sự kiện ON_DESTROY. Đây là nơi các thành phần vòng đời có thể dọn dẹp mọi thứ cần thiết trước khi Activity bị huỷ.

Thay vì đặt logic vào Activity để xác định lý do khiến Activity bị huỷ, hãy sử dụng đối tượng ViewModel để chứa dữ liệu khung hiển thị có liên quan cho Activity. Nếu Activity được tạo lại do có sự thay đổi về cấu hình, thì ViewModel không cần làm gì cả, vì ViewModel được giữ lại và chuyển cho thực thể Activity tiếp theo.

Nếu Activity không được tạo lại, thì ViewModel sẽ gọi phương thức onCleared, trong đó ViewModel có thể dọn dẹp mọi dữ liệu cần thiết trước khi bị huỷ. Bạn có thể phân biệt hai trường hợp này bằng phương thức isFinishing.

Nếu hoạt động đang hoàn tất, onDestroy là phương thức gọi lại trong vòng đời cuối cùng mà hoạt động nhận được. Nếu onDestroy được gọi do có sự thay đổi về cấu hình, thì hệ thống sẽ ngay lập tức tạo một phiên bản hoạt động mới rồi gọi onCreate trên phiên bản mới đó trong cấu hình mới.

Lệnh gọi lại onDestroy sẽ giải phóng tất cả tài nguyên chưa được giải phóng bởi các lệnh gọi lại trước đó, chẳng hạn như onStop.

Trạng thái của activity và việc loại bỏ khỏi bộ nhớ

Hệ thống sẽ loại bỏ các quy trình khi cần giải phóng RAM. Khả năng hệ thống tắt một quy trình nhất định phụ thuộc vào trạng thái của quy trình đó tại thời điểm đó. Đến lượt trạng thái của quy trình phụ thuộc vào trạng thái của hoạt động đang chạy trong quy trình. Bảng 1 cho thấy mối tương quan giữa trạng thái quy trình, trạng thái của activity và khả năng hệ thống sẽ huỷ quy trình. Bảng này chỉ áp dụng nếu một quy trình không chạy các loại thành phần ứng dụng khác.

Khả năng bị giết

Trạng thái xử lý

Trạng thái của activity cuối cùng

Thấp nhất

Nền trước (đang hoặc sắp được lấy tiêu điểm)

Đã tiếp tục

Thấp

Có thể nhìn thấy (không có tiêu điểm)

Đã bắt đầu/Đã tạm dừng

Cao hơn

Nền (không nhìn thấy)

Stopped (Đã dừng)

Cao nhất

Trống

Đã huỷ bỏ

Bảng 1. Mối quan hệ giữa vòng đời của quy trình và trạng thái của activity.

Hệ thống không bao giờ trực tiếp huỷ một hoạt động để giải phóng bộ nhớ. Thay vào đó, hệ thống sẽ huỷ quy trình mà hoạt động chạy trong đó, không chỉ huỷ hoạt động mà còn huỷ mọi thứ khác đang chạy trong quy trình. Để tìm hiểu cách duy trì và khôi phục trạng thái giao diện người dùng của hoạt động khi xảy ra sự kiện bị buộc tắt do hệ thống gây ra, hãy xem phần về lưu và khôi phục trạng thái.

Người dùng cũng có thể loại bỏ một quy trình bằng cách sử dụng Trình quản lý ứng dụng (trong phần Cài đặt) để loại bỏ ứng dụng tương ứng.

Để biết thêm thông tin về quy trình, hãy xem bài viết Tổng quan về quy trình và luồng.

Lưu và khôi phục trạng thái giao diện người dùng tạm thời

Người dùng muốn trạng thái giao diện người dùng của một hoạt động vẫn giữ nguyên trong suốt quá trình thay đổi cấu hình, chẳng hạn như khi thực hiện thao tác xoay hoặc khi chuyển sang chế độ nhiều cửa sổ. Tuy nhiên, theo mặc định, hệ thống sẽ huỷ hoạt động khi xảy ra thay đổi về cấu hình như vậy, xoá sạch mọi trạng thái giao diện người dùng đã lưu trong thực thể của hoạt động đó.

Tương tự, người dùng muốn trạng thái giao diện người dùng vẫn giữ nguyên nếu họ tạm thời chuyển từ ứng dụng của bạn sang một ứng dụng khác rồi sau đó quay lại ứng dụng của bạn. Tuy nhiên, hệ thống có thể huỷ bỏ quy trình của ứng dụng trong khi người dùng thoát ra ngoài và hoạt động của bạn bị dừng.

Khi các hạn chế của hệ thống hủy bỏ hoạt động, hãy duy trì trạng thái giao diện người dùng tạm thời của người dùng bằng cách kết hợp ViewModel (đối với logic nghiệp vụ phức tạp và trạng thái màn hình), API rememberSaveable Jetpack Compose (đối với trạng thái giao diện người dùng đơn giản) và/hoặc bộ nhớ cục bộ. Để tìm hiểu thêm về kỳ vọng của người dùng so với hành vi của hệ thống và cách tốt nhất để duy trì dữ liệu trạng thái giao diện người dùng phức tạp trong quá trình hệ thống huỷ hoạt động và bị buộc tắt quy trình, hãy xem bài viết Lưu trạng thái giao diện người dùng.

rememberSaveable tự động duy trì cả các thay đổi về cấu hình và sự kiện bị buộc tắt do hệ thống gây ra bằng cách kết hợp trạng thái ở chế độ nền, mang lại trải nghiệm liền mạch mà không cần đến mã lặp lại ở cấp hoạt động.

Trạng thái phiên bản

Có một số trường hợp hoạt động của bạn bị huỷ do hành vi bình thường của ứng dụng, chẳng hạn như khi người dùng nhấn nút Quay lại hoặc hoạt động của bạn báo hiệu việc huỷ chính nó bằng cách gọi phương thức finish.

Khi hoạt động của bạn bị huỷ do người dùng nhấn vào nút Quay lại hoặc hoạt động tự hoàn tất, cả khái niệm của hệ thống và người dùng về thực thể Activity đó sẽ biến mất vĩnh viễn. Trong những trường hợp này, kỳ vọng của người dùng sẽ khớp với hành vi của hệ thống và bạn không cần phải làm gì thêm.

Tuy nhiên, nếu hệ thống huỷ hoạt động do các hạn chế của hệ thống (chẳng hạn như thay đổi về cấu hình hoặc áp lực bộ nhớ), thì mặc dù phiên bản Activity thực tế đã biến mất, nhưng hệ thống vẫn nhớ rằng phiên bản đó đã từng tồn tại. Nếu người dùng cố gắng quay lại hoạt động này, hệ thống sẽ tạo một phiên bản mới của hoạt động đó bằng cách sử dụng một tập hợp dữ liệu đã lưu mô tả trạng thái của hoạt động khi hoạt động đó bị huỷ.

Dữ liệu đã lưu mà hệ thống dùng để khôi phục trạng thái trước đó được gọi là trạng thái thực thể. Về cơ bản, đây là một tập hợp các cặp khoá-giá trị. Theo mặc định, hệ thống sẽ dùng trạng thái thực thể để lưu thông tin cơ bản về bố cục của giao diện người dùng, chẳng hạn như nội dung người dùng nhập hoặc vị trí cuộn.

Bạn sẽ móc vào hành vi hệ thống này bằng cách sử dụng rememberSaveable. Nếu thực thể hoạt động của bạn bị huỷ và được tạo lại, thì mọi trạng thái giao diện người dùng được bao bọc trong rememberSaveable sẽ tự động được khôi phục mà bạn không cần thêm mã cấp hoạt động.

Tuy nhiên, hoạt động của bạn có thể có thông tin trạng thái phức tạp hơn mà bạn muốn khôi phục, chẳng hạn như dữ liệu người dùng, phản hồi mạng hoặc các biến thành phần theo dõi tiến trình của người dùng. Cơ chế trạng thái thực thể (và theo phần mở rộng, rememberSaveable) không phù hợp để duy trì nhiều dữ liệu hơn một lượng nhỏ, vì cơ chế này yêu cầu quá trình chuyển đổi tuần tự trên luồng chính và tiêu thụ bộ nhớ quy trình hệ thống.

Để lưu giữ nhiều dữ liệu hơn một lượng rất nhỏ, hãy kết hợp các phương pháp bằng cách sử dụng bộ nhớ cục bộ liên tục, lớp ViewModel và tính năng nâng trạng thái Compose, như trình bày trong bài viết Lưu trạng thái giao diện người dùng.

Lưu trạng thái giao diện người dùng đơn giản, gọn nhẹ bằng rememberSaveable

Khi hoạt động của bạn bắt đầu dừng, hệ thống sẽ chuẩn bị lưu thông tin trạng thái vào một gói trạng thái thực thể. Để móc nối vào hành vi này của hệ thống, bạn sử dụng rememberSaveable ngay trong các hàm có khả năng kết hợp. rememberSaveable tự động lưu và khôi phục trạng thái tạm thời của giao diện người dùng (chẳng hạn như nội dung nhập văn bản của người dùng hoặc vị trí cuộn) trong quá trình tạo lại hoạt động.

Để lưu thông tin trạng thái tuỳ chỉnh, gọn nhẹ (chẳng hạn như tiến trình của người dùng trong một trò chơi), hãy khai báo trạng thái của bạn bằng cách sử dụng rememberSaveable. Khung Compose xử lý quá trình chuyển đổi tuần tự thành gói trạng thái phiên bản ở chế độ nền:

var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) {
    mutableStateOf(
        TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length))
    )
}

Để lưu dữ liệu cố định, chẳng hạn như lựa chọn ưu tiên của người dùng hoặc dữ liệu cho cơ sở dữ liệu, hãy tận dụng các cơ hội thích hợp khi hoạt động của bạn ở nền trước. Nếu không có cơ hội như vậy, hãy lưu dữ liệu liên tục trong phương thức onStop.

Khôi phục trạng thái giao diện người dùng của hoạt động bằng trạng thái của thực thể đã lưu

Khi hoạt động của bạn được tạo lại sau khi bị huỷ trước đó, quá trình khôi phục trạng thái sẽ diễn ra tự động. Khi sử dụng rememberSaveable, bạn không cần viết bất kỳ logic khôi phục rõ ràng nào, kiểm tra các gói rỗng hoặc ghi đè lệnh gọi lại hoạt động. Mã khởi tạo và lưu trạng thái của bạn cũng khôi phục trạng thái đó một cách liền mạch khi hoạt động quay lại:

var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) {
    mutableStateOf(
        TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length))
    )
}

Hoạt động và thao tác điều hướng

Ứng dụng có thể chuyển đổi giữa các màn hình nhiều lần trong suốt vòng đời, chẳng hạn như khi người dùng nhấn nút quay lại trên thiết bị hoặc chọn một đích đến mới. Các ứng dụng Android hiện đại thường sử dụng cấu trúc hoạt động đơn. Thay vì bắt đầu một Activity mới cho mỗi màn hình, ứng dụng của bạn sẽ lưu trữ một Activity duy nhất và dùng thành phần Navigation để hoán đổi các màn hình composable trong hoạt động đó.

Để tìm hiểu cách triển khai chế độ điều hướng hiện đại, ưu tiên Compose, hãy xem hướng dẫn về thư viện Navigation 3 của Jetpack Compose.

Bắt đầu một hoạt động từ một hoạt động khác

Đôi khi, một hoạt động có thể cần bắt đầu một hoạt động khác. Nhu cầu này phát sinh, chẳng hạn như khi một ứng dụng cần di chuyển từ màn hình hiện tại sang một màn hình mới.

Tuỳ thuộc vào việc hoạt động của bạn có muốn nhận kết quả từ hoạt động mới mà hoạt động đó sắp bắt đầu hay không, bạn sẽ bắt đầu hoạt động mới bằng phương thức startActivity hoặc phương thức startActivityForResult. Trong cả hai trường hợp, bạn đều truyền vào một đối tượng Intent.

Đối tượng Intent chỉ định chính xác hoạt động mà bạn muốn bắt đầu hoặc mô tả loại hành động mà bạn muốn thực hiện. Hệ thống sẽ chọn hoạt động phù hợp cho bạn, thậm chí có thể là từ một ứng dụng khác. Đối tượng Intent cũng có thể mang theo một lượng nhỏ dữ liệu để hoạt động đã bắt đầu sử dụng. Để biết thêm thông tin về lớp Intent, hãy xem bài viết Ý định và bộ lọc ý định.

startActivity

Nếu hoạt động mới bắt đầu không cần trả về kết quả, hoạt động hiện tại có thể bắt đầu bằng cách gọi phương thức startActivity.

Khi làm việc trong ứng dụng của riêng mình, bạn thường chỉ cần chạy một hoạt động đã biết. Ví dụ: đoạn mã sau đây cho thấy cách khởi chạy một hoạt động có tên là SignInActivity.

val context = LocalContext.current

Button(onClick = {
    val intent = Intent(context, SignInActivity::class.java)
    context.startActivity(intent)
}) {
    Text("Sign In")
}

Bắt đầu các hoạt động bên ngoài

Mặc dù quá trình điều hướng trong ứng dụng do Navigation xử lý, nhưng đôi khi Activity sẽ cần bắt đầu các hoạt động khác. Điều này thường xảy ra khi bạn muốn tận dụng một ứng dụng bên ngoài để thực hiện một hành động cụ thể, chẳng hạn như mở trình duyệt web, gửi email hoặc chụp ảnh.

Để đạt được điều này, bạn sử dụng một đối tượng Intent để mô tả loại thao tác mà bạn muốn thực hiện và hệ thống sẽ khởi chạy hoạt động thích hợp từ một ứng dụng khác.

Ví dụ: nếu muốn cho phép người dùng gửi nội dung email, bạn có thể tạo ý định sau:

val intent = Intent(Intent.ACTION_SEND).apply {
    putExtra(Intent.EXTRA_EMAIL, recipientArray)
}
startActivity(intent)

Nếu bạn cần chạy một hoạt động bên ngoài và nhận lại kết quả (chẳng hạn như yêu cầu ứng dụng máy ảnh chụp ảnh và trả về hình ảnh), hãy sử dụng Activity API kết quả hiện đại thay vì lệnh gọi lại startActivityForResult không dùng nữa.

Hoạt động phối hợp

Khi một hoạt động bắt đầu một hoạt động khác, cả hai hoạt động đều trải qua các quá trình chuyển đổi vòng đời. Hoạt động đầu tiên dừng hoạt động và chuyển sang trạng thái Tạm dừng hoặc Dừng, trong khi hoạt động còn lại được tạo. Trong trường hợp các hoạt động này chia sẻ dữ liệu đã lưu vào đĩa hoặc nơi khác, bạn cần hiểu rằng hoạt động đầu tiên chưa dừng hoàn toàn trước khi hoạt động thứ hai được tạo. Thay vào đó, quá trình bắt đầu lệnh thứ hai sẽ trùng lặp với quá trình dừng lệnh đầu tiên.

Thứ tự của các lệnh gọi lại vòng đời được xác định rõ ràng, đặc biệt là khi hai hoạt động nằm trong cùng một quy trình (nói cách khác là cùng một ứng dụng) và một hoạt động đang bắt đầu hoạt động kia. Dưới đây là thứ tự các thao tác xảy ra khi Hoạt động A bắt đầu Hoạt động B:

  1. Phương thức onPause của Hoạt động A sẽ thực thi.
  2. Các phương thức onCreate, onStartonResume của Hoạt động B sẽ thực thi theo trình tự. Hoạt động B hiện được người dùng chú ý.
  3. Nếu Hoạt động A không còn hiển thị trên màn hình, phương thức onStop của hoạt động này sẽ thực thi.

Trình tự lệnh gọi lại vòng đời này cho phép bạn quản lý quá trình chuyển đổi thông tin từ hoạt động này sang hoạt động khác.

Tài nguyên khác

Để tìm hiểu thêm về vòng đời của activity, hãy xem các tài nguyên bổ sung sau:

Xem nội dung