Android Kotlin Fundamentals 04.1: Vòng đời và tính năng ghi nhật ký

Lớp học lập trình này nằm trong khóa học về Khái niệm cơ bản về Android Kotlin. Bạn sẽ nhận được nhiều giá trị nhất từ khóa học này nếu bạn làm việc qua các lớp học lập trình theo trình tự. Tất cả các lớp học lập trình trong khóa học đều có trên trang đích của các lớp học lập trình cơ bản về Android Kotlin.

Giới thiệu

Trong lớp học lập trình này, bạn tìm hiểu về một phần cơ bản của Android: hoạt động và vòng đời của mảnh. Vòng đời của hoạt động là tập hợp các trạng thái mà một hoạt động có thể ở trong suốt thời gian hoạt động. Vòng đời kéo dài từ khi hoạt động được tạo ra lúc ban đầu cho đến khi bị huỷ và khi hệ thống thu hồi tài nguyên của hoạt động đó. Khi người dùng di chuyển giữa các hoạt động trong ứng dụng của bạn (trong và ngoài ứng dụng), các 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.

Vòng đời của mảnh tương tự như vòng đời của các hoạt động. Lớp học lập trình này tập trung chủ yếu vào các hoạt động, trong đó xem xét nhanh các mảnh ở cuối.

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à điều rất quan trọng để trở thành công dân tốt của Android.

Những điều bạn nên biết

  • Hoạt động là gì và cách tạo hoạt động trong ứng dụng.
  • Phương thức onCreate() của hoạt động làm gì và loại thao tác được thực hiện trong phương thức đó.
  • Cách tạo bố cục XML cho hoạt động của bạn và cách cập nhật bố cục trong thời gian chạy.

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

  • Cách in thông tin ghi nhật ký vào Logcat (đôi khi được gọi là bảng điều khiển Android hoặc màn hình Android).
  • Thông tin cơ bản về vòng đời ActivityFragment, cũng như 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 các thao tác tại những thời điểm khác nhau trong vòng đời hoạt động.
  • Cách sử dụng thư viện Timber để đăng nhập trong ứng dụng của bạn.

Những việc bạn sẽ làm

  • Sửa đổi ứng dụng ban đầu có tên là DessertClicker để thêm thông tin ghi nhật ký được hiển thị 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 được khởi động, dừng và tiếp tục.
  • Sửa đổi ứng dụng để dùng thư viện Timber.
  • Thêm tính năng ghi nhật ký vào ứng dụng AndroidTrivia và theo dõi những thay đổi đối với trạng thái của mảnh.

Ở lớp học lập trình này, bạn làm việc với một ứng dụng ban đầu có tên là DessertClicker. Trong ứng dụng này, 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 giá trị trong bố cục về số lượng món tráng miệng đã mua và tổng số tiền người dùng đã chi.

Ứ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 sẽ đặt lại các giá trị của món tráng miệng về 0 và ứng dụng tiếp tục sử dụng tài nguyên hệ thống ngay cả khi ứng dụng đang chạy trong nền. 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.

Mỗi hoạt động và mọi mảnh đều có một thứ gọi là vòng đời. Đây là sự ám chỉ đến vòng đời của động vật, chẳng hạn như vòng đời của loài bướm này. Những trạng thái khác nhau của loài bướm cho thấy sự phát triển từ lúc sinh đến lúc trưởng thành hoàn toàn cho đến khi chết.

Tương tự như vậy, vòng đời hoạt động bao gồm các trạng thái khác nhau mà một hoạt động có thể trải qua, từ khi hoạt động được khởi tạo lần đầu tiên cho đến khi hoạt động cuối cùng bị huỷ và thu hồi bộ nhớ bởi hệ thống. 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, cũng như rời khỏi ứng dụng, trạng thái 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.

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ư AppCompatActivity, 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 đến những lệnh gọi lại này 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 của riêng mình để thực hiện các nhiệm vụ ứng với những thay đổi trạng thái vòng đời đó. Sơ đồ dưới đây cho thấy các trạng thái vòng đời cùng lệnh gọi lại có thể ghi đè.

Một mảnh cũng có vòng đời. Vòng đời của mảnh cũng tương tự như vòng đời của một hoạt động, do đó, phần lớn những gì bạn tìm hiểu được áp dụng cho cả hai. Trong lớp học lập trình này, bạn tập trung vào vòng đời của hoạt động vì đây là một phần cơ bản của Android và dễ quan sát nhất trong một ứng dụng đơn giản. Dưới đây là sơ đồ tương ứng cho vòng đời của mảnh:

Điều quan trọng là phải biết khi nào các lệnh gọi lại này được gọi 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ề những gì đang xảy ra.

Bước 1: Kiểm tra phương thức onCreate() và thêm 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 khác nhau được gọi sẽ rất hữu ích. Điều này sẽ giúp bạn tìm ra nơi diễn ra sự cố trong DessertClicker.

Một cách đơn giản để làm việc này là dùng API ghi nhật ký Android. Tính năng ghi nhật ký cho phép bạn viết các thông báo ngắn vào bảng điều khiển trong khi ứng dụng chạy và bạn có thể sử dụng bảng điều khiển này để biết khi nào các lệnh gọi lại khác nhau được kích hoạt.

  1. Tải ứng dụngDessertClicker dành cho người mới bắt đầu xuống và mở ứng dụng này trong Android Studio.
  2. Biên dịch và chạy ứng dụng, rồi nhấn nhiều lần vào hình ảnh món tráng miệng đó. Lưu ý cách giá trị của Món tráng miệng được bán và tổng số tiền thay đổi.
  3. Mở MainActivity.kt và kiểm tra phương thức onCreate() cho hoạt động này
override fun onCreate(savedInstanceState: Bundle?) {
...
}

Trong sơ đồ vòng đời hoạt động, bạn có thể nhận ra phương thức onCreate(), vì bạn đã từng sử dụng lệnh gọi lại này. Đây là phương pháp 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 sẽ tăng cường bố cục, xác định trình xử lý lượt nhấp hoặc thiết lập liên kết dữ liệu.

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

  1. Trong phương thức onCreate(), ngay sau lệnh gọi tới super.onCreate(), hãy thêm dòng sau. Nhập lớp Log nếu cần. (Nhấn Alt+Enter hoặc Option+Enter trên máy Mac rồi chọn Nhập.)
Log.i("MainActivity", "onCreate Called")

Lớp Log ghi thông báo vào Logcat. Lệnh này có ba phần:

  • Mức độ nghiêm trọng của thông điệp nhật ký, nghĩa là thông báo có vai trò quan trọng. Trong trường hợp này, phương thức Log.i() viết một thông báo thông tin. Các phương thức khác trong lớp Log bao gồm Log.e() lỗi hoặc Log.w() để cảnh báo.
  • Thẻ nhật ký, 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 báo nhật ký thực tế, một chuỗi ngắn, trong trường hợp này là "onCreate called".
  1. Biên dịch và chạy ứng dụng DessertClicker. 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.



    Logcat là bảng điều khiển để ghi nhật ký tin nhắn. Thông báo từ Android về ứng dụng của bạn sẽ xuất hiện ở đâ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.i() hoặc các phương thức lớp Log khác.
  2. Trong ngăn Logcat, hãy nhập I/MainActivity vào trường tìm kiếm.


    Logcat có thể 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. Vì bạn đã 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ý. Việc thêm I/ vào đầu có nghĩa là đây là thông báo cung cấp thông tin do Log.i() tạo.

    Thông điệp nhật ký của bạn bao gồm ngày và giờ, tên gói (com.example.android.dessertclicker), thẻ nhật ký của bạn (có I/ ở đầu) 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ướ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, onStart() có thể được gọi nhiều lần trong vòng đời của hoạt động.

Lưu ý rằng 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 của bạn 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, khi MainActivity.kt đang mở, hãy chọn Mã > Phương thức ghi đè hoặc nhấn Control+o. Một hộp thoại sẽ xuất hiện với danh sách gồm tất cả các phương thức mà bạn có thể ghi đè trong lớp này.
  2. Bắt đầu nhập onStart để tìm phương thức phù hợp. Để 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 đè theo nguyên mẫu. Mã sẽ có dạng như sau:
override fun onStart() {
   super.onStart()
}
  1. Bên trong phương thức onStart(), thêm một thông báo nhật ký:
override fun onStart() {
   super.onStart()

   Log.i("MainActivity", "onStart Called")
}
  1. Biên dịch và chạy ứng dụng DessertClicker, rồi mở ngăn Logcat. Nhập I/MainActivity vào trường tìm kiếm để lọc nhật ký. Lưu ý rằng cả hai phương thức onCreate()onStart() đều lần lượt được gọi và hoạt động của bạn được hiển thị trên màn hình.
  2. 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. Lưu ý rằng hoạt động này sẽ lại tiếp tục từ nơi đã ngừ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 ý rằng phương thức onCreate() thường không được gọi lại.

Trong tác vụ này, bạn sửa đổi ứng dụng của mình để sử dụng một thư viện ghi nhật ký phổ biến có tên là Timber. Timber có một số ưu điểm so với lớp Android Log tích hợp. Cụ thể, thư viện Timber:

  • Tạo thẻ nhật ký cho bạn dựa trên tên lớp.
  • Giúp bạn không hiển thị nhật ký trong phiên bản phát hành của ứng dụng Android.
  • Cho phép tích hợp với các thư viện báo cáo sự cố.

Bạn sẽ thấy lợi ích đầu tiên ngay lập tức; những lợi ích khác mà bạn sẽ đánh giá cao khi tạo và vận chuyển các ứng dụng lớn hơn.

Bước 1: Thêm Timber vào Gradle

  1. Hãy truy cập vào đường liên kết này đến Dự án Terra trên GitHub và sao chép dòng mã trong tiêu đề Tải xuống bắt đầu bằng từ implementation. Dòng mã sẽ trông giống như sau, mặc dù số phiên bản có thể khác.
implementation 'com.jakewharton.timber:timber:4.7.1'
  1. Trong Android Studio, trong Dự án: Chế độ xem Android, hãy mở rộng Kịch bản Gradle và mở tệp build.gradle (Mô-đun: ứng dụng).
  2. Bên trong phần Phần phụ thuộc, hãy dán dòng mã mà bạn đã sao chép.
dependencies {
   ...
   implementation 'com.jakewharton.timber:timber:4.7.1'
}
  1. Nhấp vào đường liên kết Đồng bộ hóa ngay ở phía trên cùng bên phải của Android Studio để tạo lại Gradle. Bản dựng phải thực thi mà không có lỗi.

Bước 2: Tạo một lớp Ứng dụng và khởi chạy Timber

Ở bước này, bạn tạo một lớp Application. Application là một lớp cơ sở có chứa trạng thái ứng dụng toàn cầu cho toàn bộ ứng dụng. Lớp này cũng là đối tượng chính mà hệ điều hành dùng để tương tác với ứng dụng của bạn. Có một lớp Application mặc định mà Android sử dụng nếu bạn không chỉ định lớp nào, vì vậy, sẽ luôn có một đối tượng Application được tạo cho ứng dụng của bạn mà không cần bạn làm gì đặc biệt.

Timber sử dụng lớp Application vì toàn bộ ứng dụng sẽ sử dụng thư viện ghi nhật ký này và bạn cần khởi chạy thư viện một lần trước khi thiết lập mọi thứ khác. Trong những trường hợp như vậy, bạn có thể phân lớp con cho lớp Application và ghi đè các giá trị mặc định bằng cách triển khai tùy chỉnh của riêng bạn.

Sau khi tạo lớp Application, bạn cần chỉ định lớp đó trong tệp kê khai Android.

  1. Trong gói dessertclicker, hãy tạo một lớp Kotlin mới có tên là ClickerApplication. Để thực hiện việc này, hãy mở rộng app > java rồi nhấp chuột phải vào com.example.android.creativeclicker. Chọn New > Kotlin File/Class (Tệp/Lớp Kotlin).
  2. Đặt tên cho lớp ClickerApplication và đặt Loại thành Lớp. Nhấp vào Ok.

Android Studio tạo một lớp ClickerApplication mới và mở lớp đó trong trình soạn thảo mã. Mã sẽ có dạng như sau:

package com.example.android.dessertclicker

class ClickerApplication {
}
  1. Thay đổi định nghĩa lớp thành lớp con của Application và nhập lớp Application nếu cần.
class ClickerApplication : Application() {
  1. Để ghi đè phương thức onCreate(), hãy chọn Mã > Phương thức ghi đè hoặc nhấn Control+o.
class ClickerApplication : Application() {
   override fun onCreate() {
       super.onCreate()
   }
}
  1. Bên trong phương thức onCreate() đó, hãy khởi chạy thư viện Timber:
override fun onCreate() {
    super.onCreate()

    Timber.plant(Timber.DebugTree())
}

Dòng mã này khởi tạo thư viện Timber cho ứng dụng của bạn để bạn có thể sử dụng thư viện trong các hoạt động của mình.

  1. Mở tệp AndroidManifest.xml.
  2. Ở đầu phần tử <application>, hãy thêm một thuộc tính mới cho lớp ClickerApplication để Android biết phải sử dụng lớp Application thay vì lớp mặc định.
<application
   android:name=".ClickerApplication"
...

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

Ở bước này, bạn thay đổi các lệnh gọi Log.i() để sử dụng Timber, sau đó, bạn triển khai tính năng ghi nhật ký cho mọi phương thức vòng đời khác.

  1. Mở MainActivity rồi di chuyển đến mục onCreate(). Thay thế Log.i() bằng Timber.i() và xóa thẻ nhật ký.
Timber.i("onCreate called")

Giống như lớp Log, Timber cũng sử dụng phương thức i() cho thông báo thông tin. Xin lưu ý rằng với Timber, bạn không cần thêm thẻ nhật ký; Timber tự động sử dụng tên của lớp làm thẻ nhật ký.

  1. Tương tự như vậy, hãy thay đổi lệnh gọi Log trong onStart():
override fun onStart() {
   super.onStart()

   Timber.i("onStart Called")
}
  1. Biên dịch và chạy ứng dụng DessertClicker rồi mở Logcat. Xin lưu ý rằng bạn vẫn thấy các thông điệp nhật ký giống nhau cho onCreate()onStart(), nhưng giờ đây chỉ có Timber tạo những thông báo đó chứ không phải là lớp Log.
  2. 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ý Timber cho mỗi phương thức. Đây là mã:
override fun onResume() {
   super.onResume()
   Timber.i("onResume Called")
}

override fun onPause() {
   super.onPause()
   Timber.i("onPause Called")
}

override fun onStop() {
   super.onStop()
   Timber.i("onStop Called")
}

override fun onDestroy() {
   super.onDestroy()
   Timber.i("onDestroy Called")
}

override fun onRestart() {
   super.onRestart()
   Timber.i("onRestart Called")
}
  1. Biên dịch và chạy lại DessertClicker rồi kiểm tra Logcat. Lần này, ngoài onCreate()onStart(), để ý còn có thông báo nhật ký cho phương thức gọi lại trong vòng đời onResume().

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

  • onCreate() để tạo ứng dụng.
  • onStart() để bắt đầu hoạt động và hiển thị hoạt động trên màn hình.
  • onResume() để tập trung vào hoạt động và giúp người dùng sẵn sàng tương tác với hoạt động đó.

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.

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

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 hoàn toàn đóng ứng dụng.

  1. Biên dịch và chạy ứng dụng DessertClicker, nếu bạn vẫn chưa chạy ứng dụng này. Như bạn đã thấy, các lệnh gọi lại onCreate(), onStart()onResume() được gọi khi hoạt động này bắt đầu lần đầu tiên.
  2. Nhấn vào bánh nướng một vài lần.
  3. Nhấn vào nút Quay lại trên thiết bị. Thông báo trong Logcat rằng onPause(), onStop()onDestroy() được gọi theo thứ tự.

    Trong trường hợp này, việc sử dụng nút Quay lại khiến hoạt động (và ứng dụng) bị đóng hoàn toàn. Việc thực thi phương thức onDestroy() có nghĩa là hoạt động đã bị tắt hoàn toàn và có thể được thu gom rác. Thu thập rác đề cập đến việc dọn dẹp tự động các đối tượng mà bạn sẽ không sử dụng nữa. Sau khi onDestroy() được gọi, hệ điều hành sẽ biết rằng các tài nguyên đó có thể hủy được và hệ thống sẽ bắt đầu xóa bộ nhớ đó.

    Hoạt động của bạn cũng có thể bị tắt hoàn toàn nếu mã của bạn gọi phương thức finish() của hoạt động hoặc theo cách thủ công. Hệ thống Android cũng có thể tự tắt hoạt động nếu ứng dụng của bạn không hiển thị trên màn hình trong một thời gian dài. Android thực hiện việc này để bảo tồn pin và cho phép ứng dụng khác sử dụng tài nguyên của ứng dụng của bạn.
  4. Sử dụng màn hình gần đây để quay lại ứng dụng. Đây là Logcat:


    Hoạt động đã bị hủy bỏ ở bước trước, vì vậy khi bạn quay lại ứng dụng, Android sẽ bắt đầu hoạt động mới và gọi phương thức onCreate(), onStart()onResume(). Lưu ý rằng không có số liệu thống kê DessertClicker nào từ hoạt động trước đó được giữ lại.

    Điểm mấu chốt ở đây là onCreate()onDestroy() chỉ được gọi một lần trong suốt thời gian hoạt động của một phiên bản hoạt động: onCreate() để khởi chạy ứng dụng lần đầu tiên và onDestroy() để dọn dẹp các tài nguyên mà ứng dụng của bạn sử dụng.

    Phương thức onCreate() là bước quan trọng; đây là nơi bạn thực hiện tất cả lần khởi chạy lần đầu, nơi bạn thiết lập bố cục lần đầu tiên bằng cách tăng cường biến và lần khởi chạy các biến.

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à hoàn toàn đó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 ra lần đầu tiên. Bạn cũng thấy được tất cả trạng thái của vòng đời mà hoạt động trải qua khi hoàn toàn bị tắt và huỷ. Tuy nhiên, khi người dùng tương tác với thiết bị chạy Android của họ, họ sẽ chuyển đổi giữa các ứng dụng, quay lại nhà, bắt đầu các ứ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 hoạt động đó:

  • Khi hoạt động không còn hiển thị trên màn hình, việc này được gọi là đưa hoạt động vào chạy trong 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 của bạn, hoạt động đó sẽ khởi động lại và hiển thị 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 nền, ứng dụng của bạn không được chạy chủ động để duy trì 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 của bạn 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.

Ví dụ: hãy xem xét một ứng dụng chạy mô phỏng vật lý. Hệ thống cần nhiều phép tính, được tính toán trên CPU của thiết bị, để quyết định vị trí của tất cả các đối tượng trong phần mô phỏng và hiển thị các đối tượng đó. Nếu một cuộc gọi điện thoại làm gián đoạn quá trình mô phỏng, thì người dùng có thể bị nhầm lẫn hoặc thậm chí khó chịu khi quay lại ứng dụng và thấy rằng quá trình mô phỏng đã hoàn tất.

Đó cũng là lý do cho hiệu suất này. Giả sử người dùng đã mở 20 ứng dụng sử dụng chế độ mô phỏng vật lý nhiều CPU. Nếu các ứng dụng đó\39; các hoạt động không hiện trên màn hình nhưng chúng vẫn đang thực hiện các phép tính toán hiển thị nặng trong nền, điều này sẽ làm chậm hiệu suất của toàn bộ điện thoại.

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 DessertClicker đang chạy, nhấp vào bánh nướng một vài lần.
  2. Nhấn nút Màn hình chính trên thiết bị và quan sát Logcat trong Android Studio. Khi trở lại màn hình chính, đặt ứng dụng của bạn vào chế độ nền thay vì tắt ứng dụng. Xin lưu ý rằng phương thức onPause() và các phương thức onStop() được gọi, nhưng onDestroy() thì không.


    Khi onPause() được gọi, ứng dụng sẽ không còn là tiêu điểm. Sau onStop(), ứng dụng sẽ không xuất hiện trên màn hình nữa. Mặc dù hoạt động đã dừng lại, nhưng đối tượng Activity vẫn đang nằm trong bộ nhớ, ở chế độ nền. Hoạt động không bị huỷ. Người dùng có thể quay lại ứng dụng, vì vậy, Android có thể lưu các tài nguyên hoạt động của bạn.
  3. Dùng màn hình gần đây để quay lại ứng dụng. Thông báo trong Logcat rằng hoạt động được khởi động lại bằng onRestart()onStart(), sau đó tiếp tục bằng onResume().


    Khi hoạt động quay lại nền trước, phương thức onCreate() sẽ 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() được gọi. Xin lưu ý rằng lần này khi hoạt động quay trở lại nền trước, hệ thống sẽ giữ lại số Mẫu tráng miệng.
  4. Khởi động ít nhất một ứng dụng khác ngoài DessertClicker để thiết bị có một vài ứng dụng trong màn hình gần đây.
  5. Gọi màn hình gần đây lên và mở hoạt động gần đây khác. Sau đó, quay lại các ứng dụng gần đây và đưa DessertClicker trở lại nền trước.

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

    Điểm quan trọng ở đây là onStart()onStop() được gọi nhiều lần khi người dùng chuyển đến và rời khỏi hoạt động. Bạn nên ghi đè các phương thức này để ứng dụng chuyển sang chạy trong nền hoặc khởi động lại khi ứng dụng quay về nền trước.

    Vậy còn onRestart() thì sao? Phương thức onRestart() giống onCreate(). onCreate() hoặc onRestart() được gọi trước khi hoạt động hiển thị. Phương thức onCreate() chỉ được gọi lần đầu tiên và onRestart() sau đó được gọi. Phương thức onRestart() là nơi để đặt mã mà bạn chỉ muốn gọi nếu hoạt động của bạn không được bắt đầu lần đầu tiên.

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 được 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 ứng dụng được tiếp tục và onResume() được gọi, ứng dụng sẽ tập trung vào người dùng. Một phần của vòng đời trong đó ứng dụng xuất hiện toàn màn hình và có sự tập trung của người dùng được gọi là vòng đời tương tác.

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

Sự khác biệt giữa sự chú ý và chế độ hiển thị là rất quan trọng vì một hoạt động có thể hiển thị một phần trên màn hình, nhưng lại đượ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 DessertClicker chạy, hãy nhấp vào nút Chia sẻ ở trên cùng bên phải màn hình.




    Hoạt động chia sẻ xuất hiện ở nửa dưới màn hình nhưng hoạt động này vẫn hiển thị ở nửa trên của màn hình.
  2. Hãy kiểm tra Logcat và lưu ý rằng chỉ onPause() mới được gọi.


    Trong trường hợp sử dụng này, onStop() không được gọi vì hoạt động vẫn hiển thị một phần. Tuy nhiên, hoạt động này không tập trung vào người dùng và người dùng không thể tương tác với hoạt động đó. Hoạt động "share" ở nền trước tập trung vào người dùng.

    Tại sao sự khác biệt này lại quan trọng? Hãy cân nhắc sử dụng ứng dụng vật lý. Bạn có thể muốn quá trình mô phỏng dừng khi ứng dụng chạy trong nền và tiếp tục chạy khi ứng dụng che khuất một phần. Trong trường hợp này, bạn sẽ ngừng mô phỏng trong onStop(). Nếu muốn mô phỏng ngừng khi hoạt động bị che khuất một phần, bạn sẽ đặt mã để dừng mô phỏng trong onPause().

    Bất kỳ mã nào chạy trong onPause() đều chặn các phần khác hiển thị, vì vậy hãy giữ cho mã ở mứ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.
  3. Hãy nhấp vào hộp thoại chia sẻ bên ngoài để quay lại ứng dụng và nhận thấy onResume() được gọi.

    Cả onResume()onPause() đều phải tập trung vào việc cần làm. Phương thức onResume() được gọi khi hoạt động có tiêu điểm và onPause() được gọi khi hoạt động không còn là tiêu điểm.

Vòng đời của mảnh Android tương tự như vòng đời của hoạt động, cùng với một số phương thức dành riêng cho mảnh.

Trong nhiệm vụ này, bạn xem xét ứng dụng AndroidTrivia mà bạn đã xây dựng trong các lớp học lập trình trước đó và thêm một số nhật ký để khám phá vòng đời của mảnh. Ứng dụng AndroidTrivia cho phép bạn trả lời các câu hỏi về việc phát triển Android. Nếu bạn trả lời chính xác 3 câu hỏi liên tiếp thì bạn sẽ thắng trò chơi.

Mỗi màn hình trong ứng dụng AndroidTrivia là một Fragment.

Để đơn giản hóa quy trình, bạn dùng API ghi nhật ký Android trong tác vụ này, thay vì thư viện Timber.

  1. Mở ứng dụng AndroidTrivia từ lớp học lập trình cuối cùng hoặc tải mã giải pháp AndroidTrivia xuống từ GitHub.
  2. Mở tệp TitleFragment.kt. Xin lưu ý rằng Android Studio có thể hiển thị lỗi liên kết và lỗi tệp đối chiếu chưa giải quyết cho đến khi bạn tạo lại ứng dụng.
  3. Di chuyển xuống phương thức onCreateView(). Xin lưu ý rằng ở đây là bố cục của mảnh bị thổi phồng và liên kết dữ liệu sẽ xảy ra.
  4. Thêm câu lệnh ghi nhật ký vào phương thức onCreateView(), giữa dòng vào setHasOptionsMenu() và lệnh gọi cuối cùng để quay lại:
setHasOptionsMenu(true)

Log.i("TitleFragment", "onCreateView called")

return binding.root
  1. Ngay bên dưới phương thức onCreateView(), hãy thêm câu lệnh ghi nhật ký cho từng phương thức vòng đời còn lại của mảnh. Sau đây là mã:
override fun onAttach(context: Context?) {
   super.onAttach(context)
   Log.i("TitleFragment", "onAttach called")
}
override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   Log.i("TitleFragment", "onCreate called")
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
   super.onActivityCreated(savedInstanceState)
   Log.i("TitleFragment", "onActivityCreated called")
}
override fun onStart() {
   super.onStart()
   Log.i("TitleFragment", "onStart called")
}
override fun onResume() {
   super.onResume()
   Log.i("TitleFragment", "onResume called")
}
override fun onPause() {
   super.onPause()
   Log.i("TitleFragment", "onPause called")
}
override fun onStop() {
   super.onStop()
   Log.i("TitleFragment", "onStop called")
}
override fun onDestroyView() {
   super.onDestroyView()
   Log.i("TitleFragment", "onDestroyView called")
}
override fun onDetach() {
   super.onDetach()
   Log.i("TitleFragment", "onDetach called")
}
  1. Biên dịch và chạy ứng dụng và mở Logcat.
  2. Nhập I/TitleFragment vào trường tìm kiếm để lọc nhật ký. Khi ứng dụng khởi động, Logcat sẽ trông giống như ảnh chụp màn hình sau:

Bạn có thể xem toàn bộ vòng đời khởi động của mảnh, bao gồm cả các lệnh gọi lại sau đây:

  • onAttach(): Được gọi khi mảnh được liên kết với hoạt động của chủ sở hữu.
  • onCreate(): Tương tự như onCreate() đối với hoạt động, onCreate() cho mảnh được gọi để thực hiện quy trình tạo mảnh ban đầu (ngoài bố cục).
  • onCreateView(): Được gọi để tăng cường bố cục của mảnh.
  • onActivityCreated(): Được gọi khi hoạt động của chủ sở hữu onCreate() hoàn tất. Mảnh của bạn sẽ không thể truy cập hoạt động cho đến khi phương thức này được gọi.
  • onStart(): Được gọi khi mảnh xuất hiện; song song với hoạt động onStart().
  • onResume(): Được gọi khi mảnh nhận được tiêu điểm của người dùng; song song với hoạt động onResume().
  1. Nhấn vào nút Chơi để tiếp tục chơi trò chơi đố vui và chú ý đến Logcat ngay bây giờ.

Khi bạn mở mảnh tiếp theo, mảnh tiêu đề sẽ đóng lại và các phương thức vòng đời này được gọi:

  • onPause(): Được gọi khi mảnh mất tiêu điểm của người dùng; song song với hoạt động onPause().
  • onStop(): Được gọi khi mảnh không còn hiển thị trên màn hình; song song với hoạt động onStop().
  • onDestroyView(): Được gọi khi chế độ xem của mảnh không còn cần thiết để dọn dẹp các tài nguyên liên kết với chế độ xem đó.
  1. Trong ứng dụng, hãy nhấn vào nút Lên (mũi tên ở góc trên cùng bên trái của màn hình) để quay lại mảnh tiêu đề.


    Lần này, có thể onAttach()onCreate() sẽ không được gọi để bắt đầu mảnh. Đối tượng mảnh vẫn tồn tại và vẫn được đính kèm vào hoạt động của chủ sở hữu, vì vậy vòng đời sẽ bắt đầu lại bằng onCreateView().
  2. Nhấn nút Màn hình chính của thiết bị. Lưu ý trong Logcat chỉ có onPause()onStop() được gọi. Hoạt động này tương tự như hoạt động: trang chủ sẽ đưa hoạt động và mảnh vào nền.
  3. Dùng màn hình gần đây để quay lại ứng dụng. Tương tự như đối với hoạt động, phương thức onStart()onResume() được gọi để đưa mảnh về nền trước.

Dự án Android Studio: DessertClickerLogs

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 di chuyển. Vòng đời hoạt động bắt đầu khi hoạt động được tạo lần đầu tiên và kết thúc khi hoạt động bị huỷ.
  • 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. Có 7 phương thức vòng đời:
    onCreate()
    onStart()
    onPause()
    onRestart()
    onResume()
    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 đè nòng cốt vào các lớp của bạn trong Android Studio, 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 và đặc biệt là lớp Log, cho phép bạn viết thông báo ngắn hiển thị trong Logcat trong Android Studio.
  • Sử dụng Log.i() để viết tin nhắn cung cấp thông tin. Phương thức này nhận 2 đối số: thẻ nhật ký, thường là tên của lớp và thông báo nhật ký, một chuỗi ngắn.
  • Dùng ngăn Logcat trong Android Studio để xem nhật ký hệ thống, bao gồm cả thông báo bạn viết.

Ghi nhật ký bằng Timber

Timber là một thư viện ghi nhật ký có một số ưu điểm so với API ghi nhật ký trên Android. Cụ thể, thư viện Timber:

  • Tạo thẻ nhật ký cho bạn dựa trên tên lớp.
  • Giúp tránh hiển thị nhật ký trong phiên bản phát hành của ứng dụng Android.
  • Cho phép tích hợp với các thư viện báo cáo sự cố.

Để sử dụng Timber, hãy thêm phần phụ thuộc vào tệp Gradle của bạn và mở rộng lớp Application để khởi tạo:

  • Application là lớp cơ sở chứa trạng thái ứng dụng toàn cầu cho toàn bộ ứng dụng của bạn. Có một lớp Application mặc định được Android sử dụng nếu bạn không chỉ định lớp đó. Bạn có thể tạo lớp con Application của riêng mình để khởi chạy các thư viện trên toàn ứng dụng, chẳng hạn như Timber.
  • Thêm lớp Application tùy chỉnh vào ứng dụng bằng cách thêm thuộc tính android:name vào phần tử <application> trong tệp kê khai Android. Đừng quên làm việc này!
  • Dùng Timber.i() để viết thông điệp nhật ký bằng Timber. Phương thức này chỉ nhận một đối số: thông báo cần viết. Thẻ nhật ký (tên của lớp) được thêm tự động cho bạn.

Khóa học từ Udacity:

Tài liệu dành cho nhà phát triển Android:

Các tài liệu khác:

Phần này liệt kê các bài tập về nhà có thể được giao cho học viên đang làm việc qua lớp học lập trình này trong khóa học do người hướng dẫn tổ chức. Người hướng dẫn có thể làm những việc sau:

  • Giao bài tập về nhà nếu được yêu cầu.
  • Trao đổi với học viên cách nộp bài tập về nhà.
  • Chấm điểm bài tập về nhà.

Người hướng dẫn có thể sử dụng những đề xuất này ít hay nhiều tùy ý. Do đó, họ có thể thoải mái giao bất kỳ bài tập về nhà nào khác mà họ cảm thấy phù hợp.

Nếu bạn đang tự mình làm việc qua lớp học lập trình này, hãy thoải mái sử dụng các bài tập về nhà này để kiểm tra kiến thức của bạn.

Thay đổi ứng dụng

Mở ứng dụng DiceScroller từ Bài học 1. (Bạn có thể tải ứng dụng DiceScroller tại đây nếu bạn chưa có ứng dụng.) Thêm tùy chọn hỗ trợ Timber vào ứng dụng đó, bằng cách sử dụng quy trình tương tự như cho ứng dụng DessertClicker. Ghi đè tất cả các lệnh gọi lại trong vòng đời và thêm thông điệp ghi nhật ký cho mỗi lệnh gọi lại.

Trả lời những câu hỏi này

Câu hỏi 1

Trạng thái nào sau đây KHÔNG phải là trạng thái vòng đời hoạt động?

  • Đã bắt đầu
  • Đang đợi
  • Đã tạo
  • Đã hủy

Câu hỏi 2

Phương thức vòng đời nào được gọi để hiển thị hoạt động?

  • onPause()
  • onVisible()
  • onStart()
  • onDestroy()

Câu hỏi 3

Phương thức vòng đời nào được gọi để đưa ra tâm điểm cho hoạt động?

  • onResume()
  • onVisible()
  • onStart()
  • onFocus()

Câu hỏi 4

Khi nào onCreate() được gọi trong một hoạt động?

  • Mỗi khi hoạt động hiển thị với người dùng.
  • Mỗi khi hoạt động trả về từ nền.
  • Chỉ một lần, khi hoạt động được tạo.
  • Chỉ một lần, khi hoạt động được tiếp tục.

Gửi ứng dụng để chấm điểm

Kiểm tra để đảm bảo ứng dụng có các thông tin sau:

  • Phần phụ thuộc vào Timber của tệp build.gradle cho ứng dụng.
  • Một lớp con tùy chỉnh của Application sẽ khởi chạy Timber trong onCreate().
  • Một thuộc tính cho lớp con tùy chỉnh đó trong tệp kê khai Android.
  • Phương thức ghi đè trong MainActivity cho tất cả các phương thức gọi lại trong vòng đời, với các lệnh gọi đến Timber.i() để ghi nhật ký.

Bắt đầu bài học tiếp theo: 4.2: Các tình huống vòng đời phức tạp

Để biết đường liên kết đến các lớp học lập trình khác trong khóa học này, hãy xem trang đích của các lớp học lập trình cơ bản về Android Kotlin.