处理配置变更

某些设备配置可能会在应用运行期间发生变更。这些变更包括但不限于:

  • 应用显示大小
  • 屏幕方向
  • 字体大小和粗细
  • 语言区域
  • 深色模式与浅色模式
  • 键盘可用性

其中大部分配置变更都是由某些用户互动触发的。例如,旋转或折叠设备会改变应用可用的屏幕空间量。同样,更改设备设置(例如字体大小、语言或首选主题)也会改变 Configuration 对象中的相应值。

这些参数通常需要对应用界面进行充分的更改。因此,Android 平台提供了一种专有机制来处理这种更改。 这种机制就是 Activity 重新创建

activity 重新创建

当发生配置变更时,系统会重新创建 Activity。为此,系统会调用 onDestroy 并销毁现有的 Activity 实例。随后,系统会使用 onCreate 创建一个新实例,并且这个新的 Activity 实例会使用更新后的新配置进行初始化。这也意味着,系统还会使用新配置重新创建界面。

通常,Activity 会充当可组合项的宿主。重新创建 Activity 后,Compose 也会使用新的配置值重新创建界面。

重新创建行为会自动利用与新设备配置相匹配的备用资源来自动重新加载应用,从而帮助它适应新配置。

重新创建示例

假设有这样一个可组合项,它使用字符串资源来显示静态标题:

// In the res/values/strings.xml file
// <string name="compose">Jetpack Compose</string>

// In your Compose code
Text(
    text = stringResource(R.string.compose)
)

创建 Activity 后,Text 可组合项会读取当前配置(例如语言),并解析相应的字符串资源。

如果语言发生更改,系统会重新创建 activity。发生这种情况时,Compose 会重新创建界面。由于 stringResource 会从当前配置中读取数据,因此标题会自动更新为正确的本地化值。

重新创建过程还会清除您在 Activity 中以字段形式保留的任何状态。

如需让界面状态在配置发生更改后保持不变,请使用建议的状态管理模式。使用 ViewModel 处理数据和业务逻辑,并使用 rememberSaveable 处理界面级状态。借助这些机制,您的状态会在重新创建 Activity 后保持不变,同时界面会更新以反映新配置。

如需详细了解如何在 Compose 中保存状态,请参阅 在 Compose 中保存界面状态

用户期望

应用用户会希望保留状态。如果用户在填写表单时在多窗口模式下打开另一个应用来参考信息,却在返回表单时发现表单已清空,或者直接跳转至应用的 其他位置,那么用户体验将会非常糟糕。作为开发者,您必须确保在配置变更和 activity 重新创建的整个过程中提供一致的用户体验。

如需验证应用中是否保留了状态,您可以在应用处于前台和后台时执行会导致配置变更的操作。这些操作包括:

  • 旋转设备
  • 进入多窗口模式
  • 在多窗口模式或自由窗口模式下调整应用大小
  • 折叠具有多个显示屏的可折叠设备
  • 更改系统主题,例如深色模式与浅色模式
  • 更改字体大小
  • 更改系统或应用语言
  • 连接或断开硬件键盘
  • 连接或断开基座

您可以通过多种方法在重新创建 Activity 后保留相关状态。采用哪种方法取决于您要保留的状态类型:

  • 本地持久性存储可用于处理复杂或大型数据的进程终止。持久性本地存储包括数据库或 DataStore
  • 保留的对象(如 ViewModel 实例)可在用户正在使用应用时,处理 内存中与界面相关的状态。
  • rememberSaveable 可用于在配置变更和系统发起的进程终止后保留临时界面状态。这适用于依赖于用户输入、滚动位置或导航但不属于 ViewModel 的状态。

如需详细了解各个 API 以及各自适用的使用场景, 请参阅保存界面状态

限制 activity 重新创建

您可以禁止在发生特定配置变更时自动重新创建 activity。 在仅使用 Compose 的现代应用中,界面无论如何都会重组,但建议直接处理配置变更。

默认情况下,配置变更会强制系统销毁并重新创建 activity,包括界面和从 activity 派生的任何对象。如果您声明由 activity 自行处理配置变更,系统会阻止此行为。相反,系统只会更新 Configuration 对象,而 Compose 会使用新值重组界面。

在 Compose 中直接处理配置变更具有以下几项优势:

  • 提升性能 :与完整的 activity 重新创建周期相比,重组界面的开销更低,尤其对于细微变更而言。
  • 流畅的动画 :避免重启 activity 可让您在配置变更期间运行连续动画,例如在设备旋转期间实现流畅的布局转换。
  • 状态保留 :保留 Activity 实例可降低在屏幕旋转等事件期间丢失临时界面状态的风险。请注意,您仍然必须处理系统发起的进程终止的状态保留。

如需针对特定配置变更停用 activity 重新创建功能, 请在 <activity>文件中的AndroidManifest.xml条目中将相应配置类型添加到android:configChanges。可能的值 会显示在 android:configChanges 属性的文档中。

在屏幕方向和键盘可用性发生更改时,以下清单代码会为 MyActivity 停用 Activity 重新创建功能:

<activity
    android:name=".MyActivity"
    android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
    android:label="@string/app_name">

响应配置变更

Jetpack Compose 可让应用更轻松地响应配置变更。 不过,如果您尽可能为所有配置变更停用 Activity 重新创建功能,应用仍然需要正确处理配置变更。

可以在具有 LocalConfiguration 的 Compose 界面层次结构中使用 Configuration 对象。每当发生变更时,从 LocalConfiguration.current 进行读取的可组合函数都会重组。如需详细了解 CompositionLocal 的运行方式,请参阅使用 CompositionLocal 将数据的作用域限定在局部 。

示例

在以下示例中,可组合项会显示具有特定格式的日期。该可组合项通过调用 ConfigurationCompat.getLocales来响应系统语言区域配置变更,并使用LocalConfiguration.current

@Composable
fun DateText(year: Int, dayOfYear: Int) {
    val dateTimeFormatter = DateTimeFormatter.ofPattern(
        "MMM dd",
        ConfigurationCompat.getLocales(LocalConfiguration.current)[0]
    )
    Text(
        dateTimeFormatter.format(LocalDate.ofYearDay(year, dayOfYear))
    )
}

为避免在发生语言区域更改时重新创建 Activity,托管 Compose 代码的 Activity 需要停用语言区域配置变更。为此,请将 android:configChanges 设置为 locale|layoutDirection

配置变更:关键概念和最佳实践

在处理配置变更时,您需要了解以下关键概念:

  • 配置 :设备配置用于定义界面如何向用户显示内容,例如应用显示大小、语言区域或系统主题。在 Compose 中,您可以使用 LocalConfiguration 访问配置值。
  • 配置变更 :配置会根据用户互动发生变更。例如,用户可能会更改设备设置或与设备的物理互动方式。您无法阻止配置变更。
  • Activity 重新创建 :默认情况下,配置变更会导致重新创建 Activity。这是为新配置重新初始化应用状态的内置机制。
  • Activity 销毁 :重新创建 Activity 会导致系统销毁旧 Activity 实例并创建新实例来替代它。旧实例现已过时。避免在生命周期范围之外保留对生命周期范围对象的引用。
  • 状态 :旧 Activity 实例中的状态不存在于新 Activity 实例中,因为它们是两个不同的对象实例。请勿将状态与 activity 绑定,而是使用建议的 API 来保留应用和用户状态,如保存界面状态中所述。
  • 停用 :为某种类型的配置变更停用 activity 重新创建功能需要确保应用根据新配置进行正确更新。对于大多数 Compose 应用,不建议这样做。

为了提供良好的用户体验,请遵循以下最佳实践:

  • 为配置频繁变更做好准备 :不要认为配置变更会很少发生或从不发生,无论 API 级别、设备规格或界面工具包如何。当用户导致配置变更时,他们会希望应用更新并继续使用新配置正常运行。
  • 保留状态 :重新创建 Activity 时不会丢失用户状态。请按照保存界面状态中所述的方法保留状态,使用 ViewModelrememberSaveable 等 API。
  • 避免选择退出作为快速修复: 不要选择退出 Activity 重新创建功能,这样可以轻松避免丢失状态。停用 activity 重新创建功能需要实现处理变更的承诺,而您可能会因为其他配置变更、进程终止或应用关闭所带来的 Activity 重新创建而丢失状态。因此,您无法完全停用 Activity 重新创建功能。请按照保存界面状态中所述的方法保留状态。
  • 不要回避配置变更 :不要为了回避配置变更和 Activity 重新创建,而对屏幕方向、 宽高比或尺寸可调整性施加限制。这会对希望以首选方式使用应用的用户产生负面影响。

处理基于大小的配置变更

基于大小的配置可能会随时发生变更;而且当应用在用户可以进入多窗口模式大屏设备上运行时,发生这种变更的可能性会更高。用户会希望应用能够在该环境中正常运行。

大小变更通常分为两类:显著变更和细微变更。显著的大小变更是指由于屏幕尺寸不同(例如宽度、高度或最小宽度)而将一组不同的备用资源应用于新配置。 这些资源包括应用自行定义的资源,以及从该应用所用的任何库中获取的资源。

限制为基于大小的配置变更重新创建 activity

为基于大小的配置变更停用 Activity 重新创建功能后,系统不会重新创建 Activity,而是会收到对 Activity.onConfigurationChanged 的调用。任何读取 LocalConfiguration.current 的可组合项都会自动重组以反映新大小。

如果您的清单文件中包含 android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout" ,系统为会基于大小的配置变更停用 Activity 重新创建功能。

其他资源

如需详细了解如何处理配置变更,请参阅以下其他资源:

文档

视图内容