Activity 生命周期阶段

1. 开始之前

在本 Codelab 中,您将了解 Android 的一个基本部分:Activity 生命周期

在 Activity 的生命周期中,它会经历各种状态,有时还会返回到之前的状态。这种状态转换称为 Activity 生命周期。

在 Android 中,Activity 是与用户交互的入口点。

过去,一个 Activity 会在一个应用中显示一个屏幕。根据当前最佳实践,一个 Activity 可以通过根据需要交换屏幕来显示多个屏幕。

Activity 生命周期从 Activity 创建到系统回收该 Activity 的资源时销毁,贯穿始终。当用户在 Activity 之间导航时,每个 Activity 都会在 Activity 生命周期中不同状态之间转换。

作为 Android 开发人员,您需要了解 Activity 生命周期。如果您的 Activity 未能正确响应生命周期状态变化,您的应用可能会产生奇怪的错误,导致用户感到困惑的行为,或使用过多的 Android 系统资源。了解 Android 生命周期并正确响应生命周期状态变化是 Android 开发的重要组成部分。

先决条件

  • 了解什么是 Activity 以及如何在您的应用中创建 Activity
  • 了解 Activity 的 onCreate() 方法的作用以及在该方法中执行的操作类型

您将学到什么

  • 如何将日志信息打印到 Logcat
  • Activity 生命周期基础知识以及 Activity 在状态之间转换时调用的回调函数
  • 如何重写生命周期回调方法,以便在 Activity 生命周期的不同时间执行操作

您将构建什么

  • 修改一个名为 Dessert Clicker 的起始应用,以添加显示在 Logcat 中的日志信息。
  • 重写生命周期回调方法并记录 Activity 状态的变化。
  • 运行应用并注意 Activity 启动、停止和恢复时出现的日志信息。
  • 实现 rememberSaveable 以保留在设备配置发生变化时可能丢失的应用数据。

2. 应用概述

在本 Codelab 中,您将使用名为 Dessert Clicker 的起始应用。在 Dessert Clicker 中,每次用户点击屏幕上的甜点时,应用都会为用户“购买”该甜点。应用会更新布局中的以下值:

  • 已“购买”的甜点数
  • 已“购买”甜点的总收入

245d0bdfc09f4d54.png

此应用包含几个与 Android 生命周期相关的错误。例如,在某些情况下,应用会将甜点值重置为 0。了解 Android 生命周期将帮助您理解这些问题发生的原因以及如何解决它们。

下载起始代码

在 Android Studio 中,打开 basic-android-kotlin-compose-training-dessert-clicker 文件夹。

3. 探索生命周期方法并添加基本日志记录

每个 Activity 都有一个所谓的生命周期。这个术语暗指植物和动物的生命周期,例如蝴蝶的生命周期——蝴蝶的不同状态显示了它从卵到幼虫到蛹到蝴蝶再到死亡的生长过程。

Butterfly Lifecycle - growth from egg to caterpillar to pupa to butterfly to death.

类似地,Activity 生命周期包括 Activity 可以经历的不同状态,从 Activity 首次初始化到销毁(此时操作系统 (OS) 会回收其内存)。通常,程序的入口点是 main() 方法。但是,Android Activity 从 onCreate() 方法开始;此方法相当于上述示例中的卵阶段。您已经多次在整个课程中使用过 Activity,并且您可能会识别出 onCreate() 方法。当用户启动您的应用、在 Activity 之间导航、在应用内部和外部导航时,Activity 的状态会发生变化。

下图显示了所有 Activity 生命周期状态。顾名思义,这些状态表示 Activity 的状态。请注意,与蝴蝶的生命周期不同,Activity 可以在整个生命周期中来回切换状态,而不是仅沿一个方向移动。

ca808edb1c95f07a.png

通常,您希望在 Activity 生命周期状态发生变化时更改某些行为或运行某些代码。因此,Activity 类本身以及 Activity 的任何子类(例如 ComponentActivity)都实现了一组生命周期回调方法。当 Activity 从一个状态转换到另一个状态时,Android 会调用这些回调函数,您可以在自己的 Activity 中重写这些方法,以便对这些生命周期状态变化执行任务。下图显示了生命周期状态以及可重写的回调函数。

The Activity Lifecycle scheme

了解 Android 何时调用可重写回调函数以及在每个回调方法中执行什么操作非常重要,但这两张图都比较复杂,可能会令人困惑。在本 Codelab 中,您将进行一些侦探工作,并建立对 Android Activity 生命周期的理解,而不是仅仅阅读每个状态和回调的含义。

步骤 1:检查 onCreate() 方法并添加日志记录

要弄清楚 Android 生命周期发生了什么,了解何时调用各种生命周期方法很有帮助。此信息可帮助您识别 Dessert Clicker 应用中出现问题的地方。

确定此信息的一种简单方法是使用 Android 日志记录功能。日志记录使您能够在应用运行时向控制台写入简短的消息,并使用它查看何时触发不同的回调函数。

  1. 运行 Dessert Clicker 应用,并多次点击甜点的图片。请注意 已售甜点 的值和总金额如何变化。
  2. 打开 MainActivity.kt 并检查此 Activity 的 onCreate() 方法
override fun onCreate(savedInstanceState: Bundle?) {
    // ...
}

在 Activity 生命周期图中,您可能会识别出 onCreate() 方法,因为您之前已经使用过此回调函数。它是每个 Activity 都必须实现的一种方法。onCreate() 方法是您应该对 Activity 执行任何一次性初始化的地方。例如,在 onCreate() 中,您调用 setContent(),它指定 Activity 的 UI 布局。

The onCreate Lifecycle method

onCreate() 生命周期方法仅调用一次,紧随 Activity 初始化之后——当操作系统在内存中创建新的 Activity 对象时。在 onCreate() 执行后,Activity 被认为是已创建的。

  1. MainActivity.kt 的顶层,在类声明 class MainActivity 上方,添加以下常量。

一个好的约定是在您的文件中声明一个 TAG 常量,因为它的值不会改变。

要将其标记为编译时常量,请在声明变量时使用 const。编译时常量是在编译期间已知的值。

private const val TAG = "MainActivity"
  1. onCreate() 方法中,紧随对 super.onCreate() 的调用之后,添加以下行
Log.d(TAG, "onCreate Called")
  1. 如有必要,导入 Log 类(按 Alt+Enter 或 Mac 上的 Option+Enter,然后选择 导入)。如果您启用了自动导入,则此操作会自动完成。
import android.util.Log

Log 类将消息写入 LogcatLogcat 是用于记录消息的控制台。Android 关于您的应用的消息会显示在此处,包括您使用 Log.d() 方法或其他 Log 类方法显式发送到日志的消息。

Log 指令有三个重要方面

  • 日志消息的优先级,即消息的重要性。在本例中,Log.v() 记录详细消息。Log.d() 方法写入调试消息。Log 类中的其他方法包括 Log.i()(用于信息消息)、Log.w()(用于警告)和 Log.e()(用于错误消息)。
  • 日志 标签(第一个参数),在本例中为 "MainActivity"。标签是一个字符串,使您能够更轻松地在 Logcat 中找到您的日志消息。标签通常是类的名称。
  • 实际的日志消息称为 msg(第二个参数),它是一个短字符串,在本例中为 "onCreate Called"

a4ff4aa74384ff6.png

  1. 编译并运行 Dessert Clicker 应用。当您点击甜点时,您不会在应用中看到任何行为差异。在 Android Studio 中,点击屏幕底部的 Logcat 选项卡。

cedcce52592c6665.png

  1. Logcat 窗口中,在搜索字段中键入 tag:MainActivity

37080c4e00561b0.png

Logcat 可能包含许多消息,其中大多数对您来说没有用处。您可以通过多种方式过滤 Logcat 条目,但搜索是最简单的。因为您在代码中使用了 MainActivity 作为日志标签,所以您可以使用该标签来过滤日志。您的日志消息包含日期和时间、您的日志标签、包的名称 (com.example.dessertclicker) 以及实际消息。因为此消息出现在日志中,所以您知道已执行 onCreate()

步骤 2:实现 onStart() 方法

onStart() 生命周期方法在 onCreate() 方法之后立即被调用。在 onStart() 方法运行之后,您的 Activity 就会在屏幕上可见。与仅在初始化 Activity 时调用一次的 onCreate() 方法不同,onStart() 方法在 Activity 的生命周期中可能会被系统调用多次。

a357d2291de472d9.png

请注意,onStart() 方法与对应的 onStop() 生命周期方法配对使用。如果用户启动您的应用,然后返回到设备的主屏幕,则 Activity 将停止,并且不再在屏幕上可见。

  1. 在 Android Studio 中,打开 MainActivity.kt 文件并在 MainActivity 类中放置光标,选择**代码 > 重写方法...**或按 Control+O。随即会弹出一个对话框,其中包含您可以重写此类中所有方法的冗长列表。

20c34cbad8dce892.png

  1. 开始输入 onStart 以搜索正确的方法。要滚动到下一个匹配项,请使用向下箭头。从列表中选择 onStart(),然后单击**确定**以插入样板重写代码。代码如下例所示
override fun onStart() {
    super.onStart()
}
  1. onStart() 方法内,添加一条日志消息
override fun onStart() {
    super.onStart()
    Log.d(TAG, "onStart Called")
}
  1. 编译并运行 Dessert Clicker 应用,然后打开**Logcat** 窗格。
  2. 在搜索字段中键入 tag:MainActivity 以筛选日志。请注意,onCreate()onStart() 方法都是一个接一个地被调用的,并且您的 Activity 在屏幕上可见。
  3. 按下设备上的**主页**按钮,然后使用最近使用的应用屏幕返回到 Activity。请注意,Activity 从中断的地方继续,所有值都保持不变,并且 onStart() 方法第二次记录到 Logcat。另请注意,onCreate() 方法不会再次被调用。
2024-04-26 14:54:48.721  5386-5386  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-04-26 14:54:48.756  5386-5386  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 14:55:41.674  5386-5386  MainActivity            com.example.dessertclicker           D  onStart Called

步骤 3:添加更多日志语句

在此步骤中,您将为所有其他生命周期方法实现日志记录。

  1. 重写 MainActivity 中其余的生命周期方法,并为每个方法添加日志语句,如下面的代码所示
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. 再次编译并运行 Dessert Clicker,然后检查 Logcat。

请注意,这次除了 onCreate()onStart() 之外,还有 onResume() 生命周期回调的日志消息。

2024-04-26 14:56:48.684  5484-5484  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-04-26 14:56:48.709  5484-5484  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 14:56:48.713  5484-5484  MainActivity            com.example.dessertclicker           D  onResume Called

当 Activity 从头开始时,您会看到这三个生命周期回调按顺序被调用

  • onCreate():系统创建应用时调用。
  • onStart():使应用在屏幕上可见,但用户尚无法与之交互。
  • onResume():将应用带到前台,用户现在可以与之交互。

尽管名称如此,但即使没有要恢复的内容,onResume() 方法也会在启动时被调用。

The Activity Lifecycle scheme

4. 探索生命周期用例

现在您已为 Dessert Clicker 应用设置了日志记录,您可以开始使用该应用并探索生命周期回调是如何触发的。

用例 1:打开和关闭 Activity

您从最基本的用例开始,即首次启动应用,然后关闭应用。

  1. 编译并运行 Dessert Clicker 应用(如果尚未运行)。如您所见,当 Activity 首次启动时,将调用 onCreate()onStart()onResume() 回调。
2024-04-26 14:56:48.684  5484-5484  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-04-26 14:56:48.709  5484-5484  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 14:56:48.713  5484-5484  MainActivity            com.example.dessertclicker           D  onResume Called
  1. 轻触纸杯蛋糕几次。
  2. 轻触设备上的**后退**按钮。

请注意,在 Logcat 中,onPause()onStop() 方法按此顺序被调用。

2024-04-26 14:58:19.984  5484-5484  MainActivity            com.example.dessertclicker           D  onPause Called
2024-04-26 14:58:20.491  5484-5484  MainActivity            com.example.dessertclicker           D  onStop Called
2024-04-26 14:58:20.517  5484-5484  MainActivity            com.example.dessertclicker           D  onDestroy Called

在这种情况下,使用**后退**按钮会导致 Activity(以及应用)从屏幕上移除并移至 Activity 堆栈的后面。

如果您的代码手动调用 Activity 的 finish() 方法或用户强制退出应用,Android 操作系统可能会关闭您的 Activity。例如,用户可以在最近使用的应用屏幕中强制退出或关闭应用。如果您的应用长时间未出现在屏幕上,Android 操作系统也可能会自行关闭您的 Activity。Android 这样做是为了节省电池电量并回收应用正在使用的资源,以便其他应用可以使用这些资源。这些只是 Android 系统销毁您的 Activity 的一些示例。在 Android 系统未发出警告的情况下销毁您的 Activity 时,还存在其他情况。

用例 2:从 Activity 导航到其他位置并返回

现在您已启动并关闭了应用,您已了解 Activity 首次创建时的多数生命周期状态。您还了解了 Activity 关闭时经历的多数生命周期状态。但是,当用户与 Android 设备交互时,他们会在应用之间切换、返回主页、启动新应用,并通过其他 Activity(例如电话呼叫)处理中断。

每次用户从该 Activity 导航到其他位置时,您的 Activity 不会完全关闭

  • 当您的 Activity 不再在屏幕上可见时,状态称为将 Activity 置于后台。与此相反的是,当 Activity 处于前台或屏幕上时。
  • 当用户返回到您的应用时,同一 Activity 将重新启动并再次可见。应用生命周期的这一部分称为应用的可见生命周期。

当您的应用处于后台时,通常不应处于活动运行状态,以节省系统资源和电池电量。您可以使用 Activity 生命周期及其回调来了解您的应用何时移至后台,以便您可以暂停任何正在进行的操作。然后,当您的应用进入前台时,重新启动这些操作。

在此步骤中,您将了解应用进入后台并再次返回前台时的 Activity 生命周期。

  1. 在 Dessert Clicker 应用运行时,轻触纸杯蛋糕几次。
  2. 按下设备上的**主页**按钮,并在 Android Studio 中观察 Logcat。返回主屏幕会将您的应用置于后台,而不是完全关闭应用。请注意,onPause()onStop() 方法被调用。
2024-04-26 15:00:04.905  5590-5590  MainActivity            com.example.dessertclicker           D  onPause Called
2024-04-26 15:00:05.430  5590-5590  MainActivity            com.example.dessertclicker           D  onStop Called

调用 onPause() 后,应用将不再具有焦点。在 onStop() 之后,应用将不再在屏幕上可见。尽管 Activity 已停止,但 Activity 对象仍保留在后台内存中。Android 操作系统尚未销毁 Activity。用户可能会返回到应用,因此 Android 会保留您的 Activity 资源。

c470ee28ab7f8a1a.png

  1. 使用最近使用的应用屏幕返回到应用。在模拟器上,可以通过下图所示的方形系统按钮访问最近使用的应用屏幕。

请注意,在 Logcat 中,Activity 将使用 onRestart()onStart() 重新启动,然后使用 onResume() 恢复。

bc156252d977e5ae.png

2024-04-26 15:00:39.371  5590-5590  MainActivity            com.example.dessertclicker           D  onRestart Called
2024-04-26 15:00:39.372  5590-5590  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 15:00:39.374  5590-5590  MainActivity            com.example.dessertclicker           D  onResume Called

当 Activity 返回到前台时,onCreate() 方法不会再次被调用。Activity 对象未被销毁,因此不需要再次创建。onCreate() 方法不会被调用,而是调用 onRestart() 方法。请注意,这次当 Activity 返回到前台时,**已售出的甜点**数量将被保留。

  1. 至少启动一个除 Dessert Clicker 之外的应用,以便设备在最近使用的应用屏幕中有一些应用。
  2. 调出最近使用的应用屏幕并打开另一个最近使用的 Activity。然后返回到最近使用的应用,并将 Dessert Clicker 重新带到前台。

请注意,您在这里看到的 Logcat 中的回调与按下**主页**按钮时看到的相同。onPause()onStop() 在应用进入后台时被调用,然后 onRestart()onStart()onResume() 在应用返回时被调用。

当应用停止并移至后台或应用重新启动并返回到前台时,会调用这些方法。如果您需要在这些情况下在应用中执行某些操作,则重写相关的生命周期回调方法。

用例 3:部分隐藏 Activity

您已经了解到,当应用启动并调用 onStart() 时,应用将变得在屏幕上可见。当调用 onResume() 时,应用将获得用户焦点,即用户可以与应用交互。应用完全位于屏幕上并具有用户焦点的生命周期部分称为 前台生命周期

当应用进入后台时,在 onPause() 后将失去焦点,并在 onStop() 后应用将不再可见。

焦点和可见性之间的差异非常重要。Activity 可以在屏幕上部分可见,但没有用户焦点。在此步骤中,您将查看 Activity 部分可见但没有用户焦点的其中一种情况。

  1. 在 Dessert Clicker 应用运行时,轻触屏幕右上角的**分享**按钮。

分享 Activity 将出现在屏幕的下半部分,但 Activity 仍在上半部分可见。

677c190d94e57447.pngca6285cbbe3801cf.png

  1. 检查 Logcat 并注意仅调用了 onPause()
2024-04-26 15:01:49.535  5590-5590  MainActivity            com.example.dessertclicker           D  onPause Called

在此用例中,不会调用 onStop(),因为 Activity 仍然部分可见。但是,Activity 没有用户焦点,用户无法与之交互——位于前台的“分享” Activity 具有用户焦点。

为什么这种差异很重要?仅使用 onPause() 的中断通常持续时间很短,然后返回到您的 Activity 或导航到其他 Activity 或应用。您通常希望继续更新 UI,以免应用的其余部分看起来冻结。

onPause() 中运行的任何代码都会阻止其他内容显示,因此请使 onPause() 中的代码保持轻量级。例如,如果来电,则 onPause() 中的代码可能会延迟来电通知。

  1. 轻触分享对话框外部以返回到应用,并注意 onResume() 被调用。

onResume()onPause() 都与焦点相关。当 Activity 获得焦点时,会调用 onResume() 方法;当 Activity 失去焦点时,会调用 onPause() 方法。

5. 探究配置更改

在管理 Activity 生命周期时,还有一个重要的方面需要理解:配置更改如何影响 Activity 的生命周期。

当设备状态发生根本性变化时,就会发生配置更改。此时,系统解决更改的最简单方法是完全关闭并重建 Activity。例如,如果用户更改了设备语言,则整个布局可能需要更改以适应不同的文本方向和字符串长度。如果用户将设备插入扩展坞或添加物理键盘,则应用布局可能需要利用不同的显示尺寸或布局。如果设备方向发生变化(例如,设备从纵向旋转到横向或反过来),则布局可能需要更改以适应新的方向。让我们看看应用在这种情况下如何运行。

最后一个需要演示的生命周期回调是 onDestroy(),它在 onStop() 之后调用。它在 Activity 被销毁之前调用。当应用代码调用 finish() 或系统由于配置更改需要销毁和重新创建 Activity 时,就会发生这种情况。

配置更改会导致调用 onDestroy()

屏幕旋转是导致 Activity 关闭并重新启动的一种配置更改类型。要模拟此配置更改并检查其影响,请完成以下步骤

  1. 编译并运行您的应用。
  2. 确保模拟器中的屏幕旋转锁定已禁用。
  3. 将设备或模拟器旋转到横向模式。您可以使用旋转按钮将模拟器向左或向右旋转。
  4. 检查 Logcat 并了解,当 Activity 关闭时,它会按顺序调用 onPause()onStop()onDestroy()
2024-04-26 15:03:32.183  5716-5716  MainActivity            com.example.dessertclicker           D  onPause Called
2024-04-26 15:03:32.185  5716-5716  MainActivity            com.example.dessertclicker           D  onStop Called
2024-04-26 15:03:32.205  5716-5716  MainActivity            com.example.dessertclicker           D  onDestroy Called

设备旋转时的数据丢失

  1. 编译并运行您的应用,然后打开 Logcat。
  2. 点击蛋糕几次,注意已售甜点数量和总收入不为零。
  3. 确保模拟器中的屏幕旋转锁定已禁用。
  4. 将设备或模拟器旋转到横向模式。您可以使用旋转按钮将模拟器向左或向右旋转。

11c9d83a11651608.png

  1. 检查 Logcat 中的输出。按 MainActivity 筛选输出。
2024-04-26 15:04:29.356  5809-5809  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-04-26 15:04:29.378  5809-5809  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 15:04:29.382  5809-5809  MainActivity            com.example.dessertclicker           D  onResume Called
2024-04-26 15:06:52.168  5809-5809  MainActivity            com.example.dessertclicker           D  onPause Called
2024-04-26 15:06:52.183  5809-5809  MainActivity            com.example.dessertclicker           D  onStop Called
2024-04-26 15:06:52.219  5809-5809  MainActivity            com.example.dessertclicker           D  onDestroy Called
2024-04-26 15:06:52.302  5809-5809  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-04-26 15:06:52.308  5809-5809  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 15:06:52.312  5809-5809  MainActivity            com.example.dessertclicker           D  onResume Called

注意,当设备或模拟器旋转屏幕时,系统会调用所有生命周期回调以关闭 Activity。然后,在重新创建 Activity 时,系统会调用所有生命周期回调以启动 Activity。

当设备旋转并关闭和重新创建 Activity 时,Activity 会以默认值重新启动——甜点图片、已售甜点数量和总收入重置回零。

要了解为什么这些值会被重置以及如何更正它们,您需要了解可组合项的生命周期以及它如何知道观察和保留其状态。

可组合项的生命周期

您的应用的 UI 最初是通过在一个称为组合的过程中的运行可组合函数构建的。

当应用的状态发生变化时,会调度重新组合。重新组合是指 Compose 重新执行状态可能已更改的可组合函数并创建更新的 UI。组合会更新以反映这些更改。

创建或更新组合的唯一方法是通过其初始组合和后续重新组合。

可组合函数具有自己的生命周期,它独立于 Activity 生命周期。其生命周期由以下事件组成:进入组合、重新组合 0 次或多次,然后离开组合。

为了使 Compose 能够跟踪和触发重新组合,它需要知道何时状态发生了变化。要指示 Compose 应跟踪对象的 state,该对象需要是 StateMutableState 类型。 State 类型是不可变的,只能读取。 MutableState 类型是可变的,允许读取和写入。

您已经在之前的 codelab 中的 柠檬汁应用小费计算器应用 中见过并使用过 MutableState 了。

要创建可变变量 revenue,您可以使用 mutableStateOf 声明它。 0 是其初始默认值。

var revenue = mutableStateOf(0)

虽然这足以让 Compose 在 revenue 值发生变化时触发重新组合,但它不足以保留其更新后的值。每次重新执行可组合函数时,它都会将 revenue 值重新初始化为其初始默认值 0

要指示 Compose 在重新组合期间保留和重用其值,您需要使用 remember API 声明它。

var revenue by remember { mutableStateOf(0) }

如果 revenue 的值发生变化,Compose 会为读取此值的所有可组合函数调度重新组合。

虽然 Compose 会在重新组合期间记住 revenue 状态,但在配置更改期间不会保留此状态。要使 Compose 在配置更改期间保留状态,您必须使用 rememberSaveable

有关其他练习和信息,请参阅 Compose 中的状态简介 codelab。

使用 rememberSaveable 保存跨配置更改的值

如果 Android 操作系统销毁并重新创建 Activity,则可以使用 rememberSaveable 函数保存所需的值。

要保存重新组合期间的值,您需要使用 remember。使用 rememberSaveable 保存重新组合和配置更改期间的值。

使用 rememberSaveable 保存值可确保在恢复 Activity 时(如果需要)该值可用。

  1. MainActivity 中,将当前使用 remember 的五个变量组更新为 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. 编译并运行您的应用。
  2. 点击蛋糕几次,注意已售甜点数量和总收入不为零。
  3. 将设备或模拟器旋转到横向模式。
  4. 观察在 Activity 被销毁并重新创建后,甜点图片、已售甜点数量和总收入是否恢复到之前的值。

6. 解决方案代码

7. 总结

Activity 生命周期

  • Activity 生命周期是一组 Activity 转换的 state。Activity 生命周期从 Android 操作系统首次创建 Activity 开始,到操作系统销毁 Activity 结束。
  • 当用户在 Activity 之间导航以及在应用内部和外部导航时,每个 Activity 都会在 Activity 生命周期中的 state 之间切换。
  • Activity 生命周期中的每个 state 都有一个相应的回调方法,您可以在 Activity 类中重写该方法。核心生命周期方法集包括:onCreate()onRestart()onStart()onResume()onPause()onStop()onDestroy()
  • 要添加在 Activity 转换为生命周期 state 时发生的 behavior,请重写该 state 的回调方法。
  • 要在 Android Studio 中向类添加骨架重写方法,请选择代码 > 重写方法...或按 Control+O

使用 Log 记录日志

  • Android 日志记录 API,特别是 Log 类,使您能够编写在 Android Studio 中的 Logcat 中显示的简短消息。
  • 使用 Log.d() 写入调试消息。此方法采用两个参数:日志标签(通常是类的名称)和日志消息(一个简短的字符串)。
  • 使用 Android Studio 中的Logcat窗口查看系统日志,包括您编写的消息。

配置更改

  • 当设备状态发生根本性变化时,就会发生配置更改。此时,系统解决更改的最简单方法是销毁并重建 Activity。
  • 配置更改最常见的示例是用户将设备从纵向旋转到横向模式,或从横向旋转到纵向模式。当设备语言更改或用户插入硬件键盘时,也可能发生配置更改。
  • 当发生配置更改时,Android 会调用所有 Activity 生命周期的关闭回调。然后,Android 会从头开始重新启动 Activity,并运行所有生命周期启动回调。
  • 当 Android 由于配置更改关闭应用时,它会使用 onCreate() 重新启动 Activity。
  • 要保存需要在配置更改后仍然存在的值,请使用 rememberSaveable 声明其变量。

了解更多信息