Các giai đoạn trong Vòng đời hoạt động

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

1. Trước khi bắt đầu

Trong lớp học lập trình này, bạn sẽ tìm hiểu về một phần cơ bản của Android: vòng đời hoạt động.

Trong vòng đời của mình, hoạt động sẽ trải qua và đôi khi quay lại nhiều trạng thái khác nhau. Quá trình chuyển đổi các trạng thái này được gọi là vòng đời hoạt động.

Trên Android, hoạt động là điểm truy cập để tương tác với người dùng.

Trước đây, mỗi hoạt động sẽ hiện một màn hình trong ứng dụng. Với các phương pháp hay nhất hiện tại, một hoạt động có thể cho thấy nhiều màn hình bằng cách hoán đổi các màn hình với nhau khi cần.

Vòng đời hoạt động kéo dài từ khi tạo hoạt động cho đến khi hoạt động bị huỷ, khi hệ thống thu hồi tài nguyên của hoạt động đó. Khi người dùng di chuyển vào và ra khỏi một hoạt động, mỗi hoạt động sẽ chuyển đổi giữa các trạng thái khác nhau trong vòng đời hoạt động.

Là nhà phát triển Android, bạn cần hiểu rõ vòng đời hoạt động. Nếu hoạt động không phản hồi chính xác với các thay đổi về trạng thái của vòng đời, ứng dụng có thể tạo ra các lỗi lạ, hành vi khó hiểu đối với người dùng hoặc sử dụng quá nhiều tài nguyên hệ thống của Android. Hiểu được vòng đời của Android và phản hồi chính xác với các thay đổi về trạng thái của vòng đời là một phần quan trọng trong quá trình phát triển Android.

Điều kiện tiên quyết

  • Kiến thức về khái niệm của một hoạt động và cách tạo hoạt động trong ứng dụng
  • Kiến thức về những gì phương thức onCreate() của một hoạt động thực hiện và loại thao tác được thực hiện trong phương thức đó

Kiến thức bạn sẽ học được

  • Cách in thông tin ghi nhật ký vào Logcat.
  • Thông tin cơ bản trong vòng đời của Activity và các lệnh gọi lại được gọi khi hoạt động di chuyển giữa các trạng thái
  • Cách ghi đè phương thức gọi lại trong vòng đời để thực hiện thao tác tại những thời điểm trong vòng đời hoạt động.

Sản phẩm bạn sẽ tạo ra

  • Sửa đổi ứng dụng ban đầu có tên là Dessert Clicker để thêm thông tin ghi nhật ký hiện trong Logcat.
  • Ghi đè phương thức gọi lại trong vòng đời và ghi lại các thay đổi vào trạng thái hoạt động.
  • Chạy ứng dụng và để ý thông tin ghi nhật ký xuất hiện khi hoạt động bắt đầu, dừng và tiếp tục.
  • Triển khai rememberSaveable để giữ lại dữ liệu ứng dụng có thể bị mất nếu cấu hình thiết bị thay đổi.

2. Tổng quan về ứng dụng

Ở lớp học lập trình này, bạn sẽ làm việc với một ứng dụng ban đầu có tên là Dessert Clicker. Trong ứng dụng Dessert Clicker, mỗi lần người dùng nhấn vào một món tráng miệng trên màn hình, ứng dụng sẽ "mua" món tráng miệng đó cho người dùng. Ứng dụng sẽ cập nhật các giá trị trong bố cục về:

  • Số lượng món tráng miệng được mua
  • Tổng doanh thu từ các món tráng miệng "đã mua"

4a826cc7e8ef62.png

Ứng dụng này chứa một số lỗi liên quan đến vòng đời của Android. Ví dụ: trong một số trường hợp, ứng dụng đặt lại các giá trị của món tráng miệng về 0. Khi nắm rõ vòng đời của ứng dụng Android, bạn sẽ hiểu được nguyên nhân của những vấn đề này và cách khắc phục.

Tải mã khởi động xuống

  • Trong Android Studio, hãy mở thư mục basic-android-kotlin-compose-training-dessert-clicker.

Mở mã ứng dụng Dessert Clicker trong Android Studio.

3. Khám phá các phương thức của vòng đời và thêm tính năng ghi nhật ký cơ bản

Mọi hoạt động đều có vòng đời hoạt động. Thuật ngữ này ám chỉ vòng đời của thực vật và động vật, như vòng đời của bươm bướm — các trạng thái khác nhau cho thấy sự phát triển của bươm bướm từ khi còn là trứng đến khi thành sâu bướm, rồi thành nhộng và bướm cho đến khi chết.

2e74068b69dab83f.png

Tương tự như vậy, vòng đời hoạt động bao gồm các trạng thái mà một hoạt động có thể trải qua, từ khởi tạo hoạt động lần đầu cho đến khi hoạt động bị huỷ bỏ. Tại thời điểm đó, hệ điều hành (OS) sẽ lấy lại bộ nhớ. Thông thường, điểm truy cập của chương trình là phương thức main(). Tuy nhiên, hoạt động trên Android bắt đầu bằng phương thức onCreate(). Phương thức này tương đương với giai đoạn từ khi còn là trứng trong ví dụ trên. Bạn đã sử dụng các hoạt động nhiều lần trong suốt khoá học này nên có thể bạn sẽ nhận ra phương thức onCreate(). Khi người dùng khởi động ứng dụng, di chuyển giữa các hoạt động, di chuyển trong và ngoài ứng dụng, thì trạng thái của hoạt động sẽ thay đổi.

Sơ đồ dưới đây cho thấy tất cả các trạng thái của vòng đời hoạt động. Đúng như tên gọi cho thấy, tên của các trạng thái này cho biết trạng thái của hoạt động. Lưu ý rằng không giống như vòng đời của bươm bướm, hoạt động có thể chuyển đổi qua lại giữa các trạng thái trong suốt vòng đời thay vì chỉ di chuyển theo một hướng.

ca808edb1c95f07a.png

Thường thì bạn muốn thay đổi một số hành vi hoặc chạy một mã cụ thể khi trạng thái của vòng đời hoạt động thay đổi. Do đó, chính lớp Activity và bất kỳ lớp con nào của Activity, chẳng hạn như ComponentActivity, sẽ triển khai một tập hợp các phương thức gọi lại trong vòng đời. Android gọi những lệnh gọi lại khi hoạt động chuyển từ trạng thái này sang trạng thái khác, và bạn có thể ghi đè các phương thức đó vào hoạt động để thực hiện nhiệm vụ ứng với từng thay đổi trạng thái vòng đời. Sơ đồ dưới đây cho thấy trạng thái vòng đời cùng lệnh gọi lại có thể ghi đè.

468988518c270b38.png

Điều quan trọng là phải biết khi nào Android gọi các lệnh gọi lại có thể ghi đè này và cần làm gì trong mỗi phương thức gọi lại, nhưng cả hai sơ đồ này đều phức tạp và có thể gây nhầm lẫn. Trong lớp học lập trình này, thay vì chỉ đọc ý nghĩa của từng trạng thái và lệnh gọi lại, bạn sẽ thực hiện một số thao tác để tìm hiểu và xây dựng kiến thức về vòng đời hoạt động của Android.

Bước 1: Kiểm tra phương thức onCreate() và thêm tính năng ghi nhật ký

Để tìm hiểu những gì đang xảy ra với vòng đời của Android, việc biết thời điểm các phương thức vòng đời được gọi sẽ rất hữu ích. Thông tin này giúp bạn xác định nơi diễn ra sự cố trong ứng dụng Dessert Clicker.

Cách đơn giản để xác định thông tin này là sử dụng chức năng ghi nhật ký của Android. Tính năng ghi nhật ký cho phép bạn viết thông báo ngắn vào bảng điều khiển trong khi ứng dụng chạy, đồng thời sử dụng bảng điều khiển đó để xem thời điểm các lệnh gọi lại được kích hoạt.

  1. Chạy ứng dụng Dessert Clicker và nhấn vài lần vào ảnh món tráng miệng. Lưu ý giá trị của Món tráng miệng đã bán và tổng số tiền thay đổi.
  2. Mở MainActivity.kt và kiểm tra phương thức onCreate() của hoạt động này:
override fun onCreate(savedInstanceState: Bundle?) {
    // ...
}

Trong sơ đồ vòng đời hoạt động, có thể bạn sẽ nhận ra phương thức onCreate() do đã từng sử dụng lệnh gọi lại này. Đây là phương thức mà mọi hoạt động đều phải triển khai. Phương thức onCreate() là nơi bạn nên thực hiện mọi thao tác khởi chạy một lần cho hoạt động của mình. Ví dụ: trong onCreate(), bạn gọi setContent(), trong đó chỉ định bố cục giao diện người dùng của hoạt động.

e3bc14c112d5363b.png

Phương thức vòng đời onCreate() được gọi một lần, ngay sau khi khởi chạy hoạt động (khi hệ điều hành tạo đối tượng Activity mới trong bộ nhớ). Sau khi onCreate() thực thi, hoạt động sẽ được coi là đã được tạo.

  1. Thêm hằng số sau vào cấp cao nhất của MainActivity.kt, phía trên phần khai báo lớp class MainActivity.

Có một thông lệ tốt là khai báo hằng số TAG trong tệp vì giá trị của hằng số đó sẽ không thay đổi.

Để đánh dấu hằng số này là hằng số thời gian biên dịch, hãy sử dụng const khi khai báo biến. Hằng số thời gian biên dịch là một giá trị được xác định trong quá trình biên dịch.

private const val TAG = "MainActivity"
  1. Trong phương thức onCreate(), ngay sau lệnh gọi đến super.onCreate(), hãy thêm dòng sau:
Log.d(TAG, "onCreate Called")
  1. Nhập lớp Log nếu cần (nhấn Alt+Enter hoặc Option+Enter trên máy Mac và chọn Import (Nhập)). Nếu bạn đã bật tính năng nhập tự động, thì quá trình này sẽ diễn ra một cách tự động.
import android.util.Log

Lớp Log ghi thông báo vào Logcat. Logcat là bảng điều khiển dành cho thông báo nhật ký. Thông báo của Android về ứng dụng sẽ xuất hiện tại đây, bao gồm cả các thông báo bạn gửi tới nhật ký một cách rõ ràng bằng phương thức Log.d() hoặc các phương thức lớp Log khác.

Có ba khía cạnh quan trọng của hướng dẫn Log:

  • Mức độ ưu tiên của thông báo nhật ký chính là mức độ quan trọng của thông báo. Trong trường hợp này, Log.v() sẽ ghi lại các thông báo chi tiết. Phương thức Log.d() sẽ viết một thông báo gỡ lỗi. Các phương thức khác trong lớp Log bao gồm Log.i() để thông báo thông tin, Log.w() để cảnh báo và Log.e() để thông báo lỗi.
  • Nhật ký tag (tham số đầu tiên), trong trường hợp này là "MainActivity". Thẻ là một chuỗi cho phép bạn dễ dàng tìm thấy các thông báo nhật ký trong Logcat. Thẻ thường mang tên của lớp.
  • Thông điệp nhật ký thực tế có tên msg (tham số thứ hai) là một chuỗi ngắn, trong trường hợp này là "onCreate Called".

3ed7e1a51149ef64.png

  1. Biên dịch và chạy ứng dụng Dessert Clicker. Không có khác biệt nào về hành vi trong ứng dụng khi bạn nhấn vào món tráng miệng. Trong Android Studio, ở cuối màn hình, hãy nhấp vào thẻ Logcat.

685b8a884e8e16.png.

  1. Trong cửa sổ Logcat, hãy nhập MainActivity vào trường tìm kiếm.

81a205a88b0b5906.png.

Logcat chứa nhiều thông báo, hầu hết đều không hữu ích với bạn. Bạn có thể lọc các mục logcat (entries Logcat) theo nhiều cách, nhưng tìm kiếm là cách dễ nhất. Do đã sử dụng MainActivity làm thẻ nhật ký trong mã nên bạn có thể dùng thẻ đó để lọc nhật ký. Thêm D/ vào đầu cụm từ tìm kiếm có nghĩa là chỉ lọc thông báo gỡ lỗi do Log.d() tạo. Thông điệp nhật ký bao gồm ngày và giờ, tên gói (com.example.dessertclicker), thẻ nhật ký (với D/ ở trên cùng) và thông báo thực tế. Vì thông báo này xuất hiện trong nhật ký nên bạn biết rằng onCreate() đã được thực thi.

Bạn cũng có thể lọc nhật ký theo cấp bằng cách sử dụng trình đơn thả xuống bên cạnh thanh tìm kiếm. Bạn có thể thực hiện thao tác tìm kiếm tương tự bằng cách chọn mục Debug (Gỡ lỗi) trên trình đơn thả xuống rồi nhập cùng một chuỗi truy vấn.

f27eb3f13aba6617.png

Bước 2: Triển khai phương thức onStart()

Phương thức vòng đời onStart() được gọi ngay sau onCreate(). Sau khi onStart() chạy, hoạt động của bạn sẽ hiển thị trên màn hình. Không giống như onCreate() chỉ được gọi một lần để khởi tạo hoạt động của bạn, hệ thống có thể gọi onStart() nhiều lần trong vòng đời của hoạt động.

a357d2291de472d9.png

Lưu ý là onStart() được ghép nối với phương thức vòng đời onStop() tương ứng. Nếu người dùng khởi động ứng dụng rồi quay lại màn hình chính của thiết bị, thì hoạt động sẽ dừng lại và không còn hiển thị trên màn hình.

  1. Trong Android Studio, với MainActivity.kt mở và con trỏ bên trong lớp MainActivity, hãy chọn Code (Mã)> Override Methods...(Phương thức ghi đè) hoặc nhấn vào Control+O. Một hộp thoại sẽ xuất hiện với danh sách dài tất cả các phương thức bạn có thể ghi đè trong lớp này.

44bc297e84d5a2c6.png

  1. Bắt đầu nhập onStart để tìm phương thức chính xác. Để cuộn đến mục trùng khớp tiếp theo, sử dụng mũi tên xuống. Chọn onStart() trong danh sách và nhấp vào OK để chèn mã ghi đè nguyên mẫu. Mã sẽ có dạng như trong ví dụ sau:
override fun onStart() {
    super.onStart()
}
  1. Bên trong phương thức onStart(), hãy thêm một thông báo nhật ký:
override fun onStart() {
    super.onStart()
    Log.d(TAG, "onStart Called")
}
  1. Biên dịch và chạy ứng dụng Dessert Clicker, sau đó mở ngăn Logcat.
  2. Nhập MainActivity vào trường tìm kiếm để lọc nhật ký. Hãy lưu ý rằng cả hai phương thức onCreate()onStart() đều lần lượt được gọi và hoạt động sẽ hiện trên màn hình.
  3. Nhấn vào nút Home (Màn hình chính) trên thiết bị, sau đó dùng màn hình Gần đây để quay lại hoạt động. Hãy lưu ý rằng hoạt động này sẽ lại tiếp tục từ nơi dừng lại trước đó, với cùng các giá trị và onStart() được ghi lại lần thứ hai vào Logcat. Ngoài ra, lưu ý là phương thức onCreate() sẽ không được gọi lại.
2022-07-12 08:11:29.216 16346-16346/com.example.dessertclicker D/MainActivity: onCreate Called
2022-07-12 08:11:29.344 16346-16346/com.example.dessertclicker D/MainActivity: onStart Called
2022-07-12 08:11:42.585 16346-16346/com.example.dessertclicker D/MainActivity: onStart Called

Bước 3: Thêm câu lệnh nhật ký khác

Tại bước này, bạn triển khai tính năng ghi nhật ký cho tất cả phương thức vòng đời khác.

  1. Ghi đè phần còn lại của các phương thức vòng đời trong MainActivity và thêm câu lệnh nhật ký cho từng phương thức, như trong mã sau:
override fun onResume() {
    super.onResume()
    Log.d(TAG, "onResume Called")
}

override fun onRestart() {
    super.onRestart()
    Log.d(TAG, "onRestart Called")
}

override fun onPause() {
    super.onPause()
    Log.d(TAG, "onPause Called")
}

override fun onStop() {
    super.onStop()
    Log.d(TAG, "onStop Called")
}

override fun onDestroy() {
    super.onDestroy()
    Log.d(TAG, "onDestroy Called")
}
  1. Biên dịch và chạy lại Dessert Clicker rồi kiểm tra Logcat.

Hãy lưu ý rằng lần này, ngoài onCreate()onStart() còn có thông điệp nhật ký cho phương thức gọi lại trong vòng đời onResume().

2022-07-12 08:16:13.356 16421-16421/com.example.dessertclicker D/MainActivity: onCreate Called
2022-07-12 08:16:13.424 16421-16421/com.example.dessertclicker D/MainActivity: onStart Called
2022-07-12 08:16:13.426 16421-16421/com.example.dessertclicker D/MainActivity: onResume Called

Khi một hoạt động bắt đầu từ đầu, bạn sẽ thấy cả ba lệnh gọi lại trong vòng đời này đều được gọi theo thứ tự:

  • onCreate() khi hệ thống tạo ứng dụng.
  • onStart() khiến ứng dụng xuất hiện trên màn hình nhưng người dùng chưa thể tương tác.
  • onResume() đưa ứng dụng lên nền trước và người dùng hiện có thể tương tác.

Dù có tên, phương thức onResume() vẫn được gọi khi khởi chạy, ngay cả khi không có gì để tiếp tục.

2678d691f608762a.png

4. Khám phá các trường hợp sử dụng vòng đời

Sau khi thiết lập ứng dụng Dessert Clicker để ghi nhật ký, bây giờ bạn đã sẵn sàng bắt đầu sử dụng ứng dụng và khám phá cách kích hoạt phương thức gọi lại trong vòng đời.

Trường hợp sử dụng 1: Mở và đóng hoạt động

Bạn bắt đầu với trường hợp sử dụng cơ bản nhất, đó là khởi động ứng dụng lần đầu tiên rồi đóng ứng dụng.

  1. Biên dịch và chạy ứng dụng Dessert Clicker nếu bạn vẫn chưa chạy ứng dụng này. Như đã thấy, phương thức gọi lại onCreate(), onStart()onResume() được gọi khi hoạt động khởi động lần đầu tiên.
2022-07-12 08:30:20.171 16656-16656/com.example.dessertclicker D/MainActivity: onCreate Called
2022-07-12 08:30:20.236 16656-16656/com.example.dessertclicker D/MainActivity: onStart Called
2022-07-12 08:30:20.239 16656-16656/com.example.dessertclicker D/MainActivity: onResume Called
  1. Nhấn vào bánh nướng một vài lần.
  2. Nhấn vào nút Back (Quay lại) trên thiết bị.

Bạn có thể nhận thấy trong Logcat rằng onPause()onStop() được gọi theo thứ tự.

2022-07-12 08:31:18.729 16656-16656/com.example.dessertclicker D/MainActivity: onPause Called
2022-07-12 08:31:19.301 16656-16656/com.example.dessertclicker D/MainActivity: onStop Called

Trong trường hợp này, việc sử dụng nút Back (Quay lại) sẽ khiến hoạt động (và ứng dụng) xoá khỏi màn hình và được chuyển về phía sau ngăn xếp hoạt động.

Hệ điều hành Android có thể đóng hoạt động nếu mã gọi phương thức finish() theo cách thủ công, hoặc nếu người dùng buộc thoát khỏi ứng dụng. Ví dụ: người dùng có thể buộc thoát khỏi ứng dụng hoặc đóng ứng dụng trong màn hình Gần đây. Hệ điều hành cũng có thể tự tắt hoạt động nếu ứng dụng không xuất hiện trên màn hình trong một thời gian dài. Android làm vậy để duy trì thời lượng pin và cho phép các ứng dụng khác sử dụng tài nguyên ứng dụng của bạn. Đây chỉ là một vài ví dụ về lý do hệ thống Android huỷ bỏ hoạt động của bạn. Có những trường hợp khác khi hệ thống Android huỷ bỏ hoạt động mà không đưa ra cảnh báo.

Trường hợp sử dụng 2: Rời khỏi và quay lại hoạt động

Bây giờ, khi đã khởi động và đóng ứng dụng, bạn đã thấy hầu hết các trạng thái của vòng đời khi hoạt động được tạo lần đầu tiên. Bạn cũng đã thấy hầu hết các trạng thái của vòng đời mà hoạt động trải qua khi bị đóng. Tuy nhiên, khi người dùng tương tác với thiết bị Android, họ chuyển đổi qua lại giữa các ứng dụng, quay về trang chủ, khởi động ứng dụng mới và xử lý các hoạt động gây gián đoạn bằng các hoạt động khác, như cuộc gọi điện thoại.

Hoạt động của bạn không bị đóng lại hoàn toàn mỗi khi người dùng rời khỏi:

  • Khi hoạt động không còn hiện trên màn hình, trạng thái này được gọi là đưa hoạt động vào chạy ở chế độ nền. Ngược lại với điều này là khi hoạt động diễn ra trong nền trước (foreground) hoặc trên màn hình.
  • Khi người dùng quay lại ứng dụng, hoạt động đó sẽ khởi động lại và xuất hiện trở lại. Phần này của vòng đời được gọi là vòng đời hiển thị của ứng dụng.

Khi chạy trong chế độ nền, ứng dụng không nên chủ động chạy để bảo tồn tài nguyên hệ thống và thời lượng pin. Có thể sử dụng vòng đời Activity và lệnh gọi lại để biết ứng dụng chuyển sang chạy trong chế độ nền khi nào, để có thể tạm dừng mọi hoạt động đang diễn ra. Sau đó, khởi động lại các thao tác khi ứng dụng của bạn xuất hiện trên nền trước.

Tại bước này, bạn xem xét vòng đời hoạt động khi ứng dụng chuyển sang chạy ở chế độ nền và trở lại nền trước.

  1. Khi ứng dụng Dessert Clicker đang chạy, hãy nhấp vào cupcake một vài lần.
  2. Nhấn nút Home (Màn hình chính) trên thiết bị của bạn và quan sát Logcat trong Android Studio. Việc trở lại màn hình chính đặt ứng dụng vào chế độ nền thay vì hoàn toàn tắt ứng dụng. Lưu ý là phương thức onPause()onStop() được gọi.
2022-07-12 11:49:48.593 18350-18350/com.example.dessertclicker D/MainActivity: onPause Called
2022-07-12 11:49:49.191 18350-18350/com.example.dessertclicker D/MainActivity: onStop Called

Khi onPause() được gọi, ứng dụng sẽ không còn ở tâm điểm nữa. Sau onStop(), ứng dụng không còn hiển thị trên màn hình nữa. Dù hoạt động đã dừng, nhưng đối tượng Activity vẫn còn trong bộ nhớ ở chế độ nền. Hệ điều hành Android chưa huỷ hoạt động đó. Người dùng có thể quay lại ứng dụng, vì vậy, Android vẫn duy trì các tài nguyên hoạt động của bạn.

c470ee28ab7f8a1a.png

  1. Dùng màn hìnhGần đây để quay lại ứng dụng. Trên trình mô phỏng, bạn có thể truy cập vào màn hình Gần đây bằng nút hệ thống hình vuông có trong hình dưới đây.

Lưu ý trong Logcat là hoạt động khởi động lại với onRestart()onStart(), sau đó tiếp tục với onResume().

1ce2c5a03ad43e14.png

2022-07-12 11:50:54.787 18350-18350/com.example.dessertclicker D/MainActivity: onRestart Called
2022-07-12 11:50:54.789 18350-18350/com.example.dessertclicker D/MainActivity: onStart Called
2022-07-12 11:50:54.791 18350-18350/com.example.dessertclicker D/MainActivity: onResume Called

Khi hoạt động quay lại nền trước, phương thức onCreate() không được gọi lại. Đối tượng hoạt động không bị huỷ, do đó không cần được tạo lại. Thay vì onCreate(), phương thức onRestart() sẽ được gọi. Lần này, hãy lưu ý rằng khi hoạt động quay lại nền trước, số lượng Desserts sold (Món tráng miệng đã bán) sẽ được giữ lại.

  1. Khởi động ít nhất một ứng dụng khác không phải là Dessert Clicker để thiết bị có một vài ứng dụng trong màn hình Gần đây.
  2. Hiển thị màn hình Gần đây và mở một hoạt động khác gần đây. Sau đó, quay lại các ứng dụng gần đây và đưa Dessert Clicker trở lại nền trước.

Hãy lưu ý rằng bạn cũng thấy các lệnh gọi lại này trong Logcat tại đây khi nhấn vào nút Home (Màn hình chính). onPause()onStop() được gọi khi ứng dụng chuyển sang chạy ở chế độ nền, sau đó onRestart(), onStart()onResume() được gọi khi ứng dụng hoạt động trở lại.

Các phương thức này được gọi khi ứng dụng dừng và chuyển sang chạy ở chế độ nền, hoặc khi ứng dụng khởi động lại và quay lại nền trước. Nếu bạn cần thực hiện một số thao tác trong ứng dụng trong những trường hợp này, hãy ghi đè phương thức gọi lại trong vòng đời có liên quan.

Trường hợp sử dụng 3: Ẩn một phần hoạt động

Bạn biết rằng khi một ứng dụng khởi động và onStart() được gọi, ứng dụng này sẽ được hiển thị trên màn hình. Khi onResume() được gọi, ứng dụng sẽ thu hút tâm điểm chú ý của người dùng, nghĩa là người dùng có thể tương tác với ứng dụng. Một phần của vòng đời, mà trong đó ứng dụng được hiển thị trên toàn màn hình và thu hút tâm điểm chú ý của người dùng, được gọi là thời gian tồn tại ở nền trước.

Khi ứng dụng chuyển sang chạy trong chế độ nền, tâm điểm sẽ mất đi sau onPause() và ứng dụng không còn hiển thị sau onStop() nữa.

Sự khác biệt giữa tâm điểm và chế độ hiển thị rất quan trọng. Một hoạt động có thể hiển thị một phần trên màn hình nhưng không có tiêu điểm của người dùng (không được người dùng chú ý). Ở bước này, bạn xem xét trường hợp trong đó một hoạt động được hiển thị một phần nhưng không được người dùng chú ý.

  1. Khi ứng dụng Dessert Clicker đang chạy, hãy nhấp vào nút Share (Chia sẻ) ở góc trên bên phải màn hình.

Hoạt động chia sẻ xuất hiện ở nửa dưới của màn hình nhưng hoạt động vẫn hiển thị ở nửa trên.

206854c62b507676.png6b50c3897f1cdef1.png.

  1. Kiểm tra Logcat và lưu ý rằng chỉ onPause() mới được gọi.
2022-07-12 12:14:10.864 18350-18350/com.example.dessertclicker D/MainActivity: onPause Called

Trong trường hợp sử dụng này, onStop() không được gọi vì hoạt động vẫn chỉ được hiển thị một phần. Tuy nhiên, hoạt động không được người dùng chú ý và người dùng không thể tương tác với hoạt động đó. Hoạt động "chia sẻ" ở nền trước (foreground) có được sự chú ý của người dùng.

Tại sao sự khác biệt này lại quan trọng? Quá trình gián đoạn với riêng onPause() thường kéo dài trong một thời gian ngắn trước khi quay lại hoạt động của bạn hoặc chuyển đến hoạt động hay ứng dụng khác. Thông thường, bạn muốn tiếp tục cập nhật giao diện người dùng để phần còn lại của ứng dụng không tỏ ra bị treo.

Dù mã nào chạy trong onPause() thì đều chặn nội dung khác hiển thị, vì vậy, hãy bảo đảm mã trong onPause() có kích thước nhỏ. Ví dụ: nếu có cuộc gọi điện thoại đến, mã trong onPause() có thể trì hoãn thông báo cuộc gọi đến.

  1. Nhấp vào bên ngoài hộp thoại chia sẻ để quay lại ứng dụng và để ý rằng onResume() được gọi.

Cả onResume()onPause() đều liên quan đến tâm điểm. Phương thức onResume() được gọi khi hoạt động nhận được tiêu điểm và onPause() được gọi khi hoạt động mất đi tiêu điểm.

5. Khám phá thay đổi về cấu hình

Có một trường hợp khác trong việc quản lý vòng đời hoạt động mà bạn cần phải nắm vững, đó là tác động của các thay đổi trong cấu hình đối với vòng đời của hoạt động.

Thay đổi cấu hình xảy ra khi trạng thái của thiết bị hoàn toàn thay đổi đến mức cách dễ nhất để hệ thống giải quyết vấn đề là hoàn toàn tắt đi và tạo lại hoạt động. Ví dụ: nếu người dùng thay đổi ngôn ngữ của thiết bị, thì toàn bộ bố cục cần phải thay đổi cho phù hợp với các hướng văn bản và độ dài chuỗi. Nếu người dùng cắm thiết bị vào đế sạc hoặc thêm bàn phím thực, thì bố cục ứng dụng có thể cần tận dụng bố cục hoặc kích thước hiển thị khác. Còn nếu hướng thiết bị thay đổi (nếu thiết bị được xoay từ dọc sang ngang hoặc quay lại theo hướng khác) thì bố cục có thể cần thay đổi để phù hợp với hướng mới. Hãy cùng xem cách ứng dụng hoạt động trong tình huống này.

Lệnh gọi lại gần nhất trong vòng đời để minh hoạ là onDestroy(), được gọi sau onStop(). Lệnh này được gọi ngay trước khi hoạt động bị huỷ. Điều này có thể xảy ra khi mã của ứng dụng gọi finish(), hoặc khi hệ thống cần huỷ và tạo lại hoạt động do thay đổi cấu hình.

Việc thay đổi cấu hình khiến onDestroy() được gọi

Xoay màn hình là một dạng thay đổi cấu hình khiến hoạt động bị tắt và khởi động lại. Để mô phỏng và kiểm tra mức độ ảnh hưởng của sự thay đổi về cấu hình này, hãy hoàn thành các bước sau:

  1. Biên dịch và chạy ứng dụng của bạn.
  2. Đảm bảo bạn đã tắt tuỳ chọn khoá xoay màn hình trong trình mô phỏng.
  3. Xoay thiết bị hoặc trình mô phỏng sang chế độ ngang. Bạn có thể xoay trình mô phỏng sang trái hoặc sang phải bằng các nút xoay.
  4. Kiểm tra Logcat và hiểu rằng khi hoạt động tắt, nó sẽ gọi onPause(), onStop()onDestroy() theo thứ tự.
2022-07-16 20:03:31.147 30587-30587/com.example.dessertclicker D/MainActivity: onPause Called
2022-07-16 20:03:31.155 30587-30587/com.example.dessertclicker D/MainActivity: onStop Called
2022-07-16 20:03:31.184 30587-30587/com.example.dessertclicker D/MainActivity: onDestroy Called

Mất dữ liệu khi xoay thiết bị

  1. Biên dịch và chạy ứng dụng của bạn, sau đó mở Logcat.
  2. Nhấp vào cupcake vài lần và lưu ý rằng các món tráng miệng đã bán và tổng doanh thu là một số khác 0.
  3. Đảm bảo bạn đã tắt tuỳ chọn khoá xoay màn hình trong trình mô phỏng.
  4. Xoay thiết bị hoặc trình mô phỏng sang chế độ ngang. Bạn có thể xoay trình mô phỏng sang trái hoặc sang phải bằng các nút xoay.

8a5b471d2b2359a8.png

  1. Kiểm tra kết quả đầu ra trong Logcat. Lọc kết quả đầu ra trên MainActivity.
2022-07-12 12:31:55.098 19326-19326/com.example.dessertclicker D/MainActivity: onCreate Called
2022-07-12 12:31:55.152 19326-19326/com.example.dessertclicker D/MainActivity: onStart Called
2022-07-12 12:31:55.154 19326-19326/com.example.dessertclicker D/MainActivity: onResume Called
2022-07-12 12:31:59.600 19326-19326/com.example.dessertclicker D/MainActivity: onPause Called
2022-07-12 12:31:59.608 19326-19326/com.example.dessertclicker D/MainActivity: onStop Called
2022-07-12 12:31:59.644 19326-19326/com.example.dessertclicker D/MainActivity: onDestroy Called
2022-07-12 12:31:59.668 19326-19326/com.example.dessertclicker D/MainActivity: onCreate Called
2022-07-12 12:31:59.678 19326-19326/com.example.dessertclicker D/MainActivity: onStart Called
2022-07-12 12:31:59.679 19326-19326/com.example.dessertclicker D/MainActivity: onResume Called

Lưu ý rằng khi thiết bị hoặc trình mô phỏng xoay màn hình, hệ thống sẽ gọi tất cả các phương thức gọi lại trong vòng đời để tắt hoạt động. Sau đó, khi hoạt động được tạo lại, hệ thống sẽ gọi tất cả phương thức gọi lại trong vòng đời để khởi động hoạt động.

Khi xoay thiết bị, hoạt động bị tắt và được tạo lại, hoạt động khởi động lại với các giá trị mặc định, hình ảnh món tráng miệng, số lượng món tráng miệng đã bán và tổng doanh thu được đặt lại bằng không.

Để tìm hiểu lý do khiến các giá trị được đặt lại và cách chỉnh sửa, bạn cần tìm hiểu về vòng đời của một thành phần kết hợp và cách thành phần này quan sát đồng thời bảo tồn trạng thái.

Vòng đời của một thành phần kết hợp

Giao diện người dùng của ứng dụng ban đầu được xây dựng từ việc chạy các hàm có khả năng kết hợp trong một quy trình có tên là Cấu trúc (thành phần Compose).

Khi trạng thái của ứng dụng thay đổi, quá trình kết hợp lại sẽ được lên lịch. Quá trình kết hợp lại xảy ra khi Compose thực thi lại các hàm có khả năng kết hợp sở hữu trạng thái có thể đã thay đổi, đồng thời tạo giao diện người dùng được cập nhật. Thành phần Compose được cập nhật để phản ánh những thay đổi này.

Cách duy nhất để tạo hoặc cập nhật một thành phần Compose là sử dụng thành phần kết hợp ban đầu cùng với các thành phần kết hợp lại sau đó.

Các hàm có khả năng kết hợp có vòng đời riêng độc lập với Vòng đời hoạt động. Vòng đời của các hàm này bao gồm những sự kiện: nhập vào thành phần Compose, kết hợp lại từ 0 lần trở lên, sau đó rời khỏi thành phần Compose.

Để Compose có thể theo dõi và kích hoạt quá trình kết hợp lại, Compose cần biết thời điểm trạng thái thay đổi. Để cho Compose biết rằng cần phải theo dõi trạng thái của một đối tượng, thì đối tượng đó phải thuộc loại State hoặc MutableState. State là loại không thể thay đổi và chỉ có thể đọc được. MutableState là loại có thể thay đổi đồng thời cho phép đọc và ghi.

Bạn đã xem và sử dụng MutableState trong ứng dụng Lemonadeứng dụng Tip Time ở các lớp học lập trình trước đó.

Để tạo biến có thể thay đổi revenue, bạn khai báo biến này bằng cách sử dụng mutableStateOf. 0 là giá trị mặc định ban đầu của biến.

var revenue = mutableStateOf(0)

Mặc dù thao tác này có thể giúp Compose kích hoạt quá trình kết hợp lại khi giá trị doanh thu thay đổi, nhưng vẫn chưa đủ khả năng để giữ lại giá trị cập nhật. Do đó, mỗi lần thực thi lại, thành phần kết hợp sẽ khởi tạo lại giá trị doanh thu về giá trị mặc định ban đầu là 0.

Để hướng dẫn Compose giữ lại và tái sử dụng giá trị trong quá trình kết hợp lại, bạn cần khai báo giá trị này bằng API remember.

var revenue by remember { mutableStateOf(0) }

Nếu giá trị của revenue thay đổi, thì Compose sẽ lên lịch cho tất cả hàm có khả năng kết hợp đọc giá trị này để kết hợp lại.

Mặc dù Compose ghi nhớ trạng thái doanh thu trong quá trình kết hợp lại nhưng nó sẽ không giữ lại trạng thái này trong quá trình thay đổi cấu hình. Để Compose giữ lại trạng thái trong quá trình thay đổi cấu hình, bạn phải sử dụng rememberSaveable.

Để biết thêm thông tin và thực hành, vui lòng tham khảo lớp học lập trình "Giới thiệu về trạng thái trong Compose".

Sử dụng rememberSaveable để lưu giá trị qua các lần thay đổi cấu hình

Bạn sẽ sử dụng hàm rememberSaveable để lưu các giá trị mình cần nếu hệ điều hành Android huỷ bỏ và tạo lại hoạt động.

Để lưu giá trị trong quá trình kết hợp lại, bạn cần sử dụng remember. Dùng rememberSaveable để lưu giá trị trong quá trình kết hợp lại VÀ thay đổi cấu hình.

Việc lưu giá trị bằng cách sử dụng rememberSaveable bảo đảm rằng giá trị này luôn có sẵn ngay khi hoạt động được khôi phục.

  1. Trong MainActivity, hãy cập nhật nhóm 5 biến hiện đang sử dụng remember thành rememberSaveable.
var revenue by remember { mutableStateOf(0) }
...
var currentDessertImageId by remember {
    mutableStateOf(desserts[currentDessertIndex].imageId)
}
var revenue by rememberSaveable { mutableStateOf(0) }
...
var currentDessertImageId by rememberSaveable {
    mutableStateOf(desserts[currentDessertIndex].imageId)
}
  1. Biên dịch và chạy ứng dụng của bạn.
  2. Nhấp vào cupcake vài lần và lưu ý rằng các món tráng miệng đã bán và tổng doanh thu là một số khác 0.
  3. Xoay thiết bị hoặc trình mô phỏng sang chế độ ngang.
  4. Quan sát thấy sau khi hoạt động bị huỷ bỏ và được tạo lại, hình ảnh món tráng miệng, món tráng miệng đã bán và tổng doanh thu được khôi phục về giá trị trước đó.

6. Mã giải pháp

7. Tóm tắt

Vòng đời hoạt động

  • Vòng đời hoạt động là một tập hợp các trạng thái mà qua đó một hoạt động sẽ chuyển đổi. Vòng đời hoạt động bắt đầu khi hệ điều hành Android tạo hoạt động lần đầu tiên và kết thúc khi hệ điều hành huỷ hoạt động.
  • Khi người dùng di chuyển giữa các hoạt động, bên trong và bên ngoài ứng dụng, mỗi hoạt động sẽ di chuyển giữa các trạng thái trong vòng đời hoạt động.
  • Mỗi trạng thái trong vòng đời hoạt động đều có một phương thức gọi lại tương ứng mà bạn có thể ghi đè trong lớp Activity. Tập hợp cốt lõi các phương thức vòng đời là: onCreate()onRestart()onStart()onResume()onPause()onStop()onDestroy()
  • Để thêm hành vi xảy ra khi hoạt động chuyển đổi sang trạng thái vòng đời, ghi đè phương thức gọi lại của trạng thái.
  • Để thêm phương thức ghi đè skeleton vào các lớp trong Android Studio, hãy chọn Code (Mã) > Override Methods (Phương thức ghi đè) hoặc nhấn vào Control+O.

Ghi nhật ký bằng Nhật ký

  • API ghi nhật ký Android, đặc biệt là lớp Log, cho phép bạn viết các thông báo ngắn được hiển thị trong Logcat bên trong Android Studio.
  • Sử dụng Log.d() để viết thông báo gỡ lỗi. Phương thức này sẽ nhận hai đối số: thẻ nhật ký, thường là tên lớp và nhật ký thông báo, thường là một chuỗi ngắn.
  • Sử dụng cửa sổ Logcat trong Android Studio để xem nhật ký hệ thống, bao gồm cả các thông báo bạn từng viết.

Thay đổi cấu hình

  • Thay đổi cấu hình xảy ra khi trạng thái của thiết bị hoàn toàn thay đổi đến mức cách dễ nhất để hệ thống giải quyết thay đổi là huỷ và tạo lại hoạt động.
  • Ví dụ phổ biến nhất về việc thay đổi cấu hình là khi người dùng xoay thiết bị từ chế độ dọc sang chế độ ngang, hoặc từ chế độ ngang sang chế độ dọc. Việc thay đổi cấu hình cũng có thể xảy ra khi ngôn ngữ của thiết bị thay đổi hoặc người dùng kết nối với bàn phím phần cứng.
  • Khi xảy ra thay đổi cấu hình, Android sẽ gọi tất cả lệnh gọi lại tắt (shutdown callback) của vòng đời hoạt động. Sau đó, Android sẽ khởi động lại hoạt động từ đầu, chạy tất cả các lệnh gọi lại khởi động vòng đời.
  • Khi Android tắt ứng dụng do thay đổi cấu hình, ứng dụng sẽ khởi động lại hoạt động bằng onCreate().
  • Để lưu một giá trị cần tồn tại sau khi thay đổi cấu hình, hãy khai báo các biến của giá trị bằng rememberSaveable.

Tìm hiểu thêm