始终开启的应用与系统氛围模式

本指南介绍了如何让应用保持开启状态、如何响应电源状态转换,以及如何管理应用行为,以便在节省电量的同时提供良好的用户体验。

让应用始终可见会显著影响电池续航时间,因此在添加此功能时,请考虑电源影响。

关键概念

当 Wear OS 应用全屏显示时,它会处于以下两种电源状态之一:

  • Interactive:屏幕处于全亮度的高功耗状态,允许用户进行完整互动。
  • 氛围:一种低功耗状态,显示屏会调暗以节省电量。在此状态下,应用的界面仍会占据整个屏幕,但系统可能会通过模糊处理或叠加时间等内容来改变其外观。这也称为氛围模式

操作系统会控制这些状态之间的转换。

始终开启的应用是指在互动氛围状态下都显示内容的应用。

当始终开启的应用在设备处于低功耗氛围状态时继续显示自己的界面时,系统会将其描述为处于氛围模式

系统转换和默认行为

当应用位于前台时,系统会根据用户闲置触发的两个超时来管理电源状态转换。

  • 超时 1:从 Interactive 状态转换为 Ambient 状态:在用户处于非活动状态的一段时间后,设备会进入 Ambient 状态。
  • 超时 2:返回表盘:在用户继续闲置一段时间后,系统可能会隐藏当前应用并显示表盘。

在系统通过首次转换进入氛围状态后,默认行为取决于 Wear OS 版本和应用的配置:

  • 在 Wear OS 5 及更低版本中,系统会显示已暂停应用的模糊屏幕截图,并在顶部叠加时间。
  • 在 Wear OS 6 及更高版本中,如果应用以 SDK 36 或更高版本为目标平台,则系统会将其视为始终开启。显示屏会变暗,但应用会继续运行并保持可见。(更新频率可能低至每分钟一次。)

自定义氛围模式的行为

无论默认系统行为如何,在所有 Wear OS 版本中,您都可以使用 AmbientLifecycleObserver 监听状态转换的回调,以便在氛围状态下自定义应用的外观或行为。

使用 AmbientLifecycleObserver

如需响应氛围模式事件,请使用 AmbientLifecycleObserver 类:

  1. 实现 AmbientLifecycleObserver.AmbientLifecycleCallback 接口。使用 onEnterAmbient() 方法调整界面以适应低功耗状态,并使用 onExitAmbient() 将其恢复为全交互显示。

    val ambientCallback = object : AmbientLifecycleObserver.AmbientLifecycleCallback {
        override fun onEnterAmbient(ambientDetails: AmbientLifecycleObserver.AmbientDetails) {
            // ... Called when moving from interactive mode into ambient mode.
            // Adjust UI for low-power state: dim colors, hide non-essential elements.
        }
    
        override fun onExitAmbient() {
            // ... Called when leaving ambient mode, back into interactive mode.
            // Restore full UI.
        }
    
        override fun onUpdateAmbient() {
            // ... Called by the system periodically (typically once per minute)
            // to allow the app to update its display while in ambient mode.
        }
    }
    
  2. 创建 AmbientLifecycleObserver 并将其注册到 activity 或可组合项的生命周期。

    private val ambientObserver = AmbientLifecycleObserver(activity, ambientCallback)
    
    override fun onCreate(savedInstanceState: Bundle) {
        super.onCreate(savedInstanceState)
        lifecycle.addObserver(ambientObserver)
    
        // ...
    }
    
  3. 调用 removeObserver() 以移除 onDestroy() 中的观察器。

对于使用 Jetpack Compose 的开发者,Horologist 库提供了一个实用的实用程序:AmbientAware 可组合项,可简化此模式的实现。

环境感知 TimeText

TimeText 微件在 Wear OS 6 中具有氛围感知功能,是需要自定义观察器的例外情况。当设备处于氛围状态时,它会每分钟自动更新一次,而无需任何额外的代码。

控制屏幕开启时长

以下部分介绍了如何管理应用在屏幕上停留的时长。

阻止返回包含持续性活动的表盘

氛围状态(超时 #2)下停留一段时间后,系统通常会返回到表盘。用户可以在系统设置中配置超时时长。对于某些用例(例如用户跟踪锻炼情况),应用可能需要保持较长时间的显示状态。

在 Wear OS 5 及更高版本中,您可以通过实现持续性活动来防止这种情况。如果您的应用正在显示有关持续性用户任务(例如锻炼时段)的信息,您可以使用 Ongoing Activity API 让您的应用在任务结束之前保持可见状态。如果用户手动返回表盘,持续性活动指示器可让用户一键返回您的应用

如需实现此功能,持续性通知的触摸 intent 必须指向您的始终开启 activity,如以下代码段所示:

private fun createNotification(): Notification {
    val activityIntent =
        Intent(this, AlwaysOnActivity::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
        }

    val pendingIntent =
        PendingIntent.getActivity(
            this,
            0,
            activityIntent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
        )

    val notificationBuilder =
        NotificationCompat.Builder(this, CHANNEL_ID)
            // ...
            // ...
            .setOngoing(true)

    // ...

    val ongoingActivity =
        OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder)
            // ...
            // ...
            .setTouchIntent(pendingIntent)
            .build()

    ongoingActivity.apply(applicationContext)

    return notificationBuilder.build()
}

让屏幕保持开启状态并防止进入氛围模式

在极少数情况下,您可能需要完全阻止设备进入氛围模式。也就是说,要避免超时 1。为此,您可以使用 FLAG_KEEP_SCREEN_ON 窗口标志。这会用作唤醒锁,将设备保持在互动状态。请务必谨慎使用此功能,因为它会严重影响电池续航时间。

针对氛围模式的建议

为了在氛围模式下提供最佳用户体验并节省电量,请遵循以下设计准则。

  • 使用极简、低功耗的显示屏
    • 至少让 85% 的屏幕保持黑色。
    • 为大图标或按钮使用轮廓,而不是实心填充。
    • 仅显示最重要的信息,将次要详细信息移至互动显示屏。
    • 避免使用大面积的纯色和无实用价值的品牌或背景图片。
  • 确保内容适当更新
    • 对于秒表、锻炼距离或时间等频繁变化的数据,请显示 -- 等占位符内容,以免给人造成内容是最新内容的印象。
    • 移除持续更新的进度指示器,例如倒计时环和媒体会话。
    • onUpdateAmbient() 回调应仅用于基本更新,通常每分钟一次。
  • 保持一致的布局
    • 让元素在交互模式和氛围模式下保持相同的位置,以实现流畅的转换。
    • 始终显示时间。
  • 具有情境感知
    • 如果用户在设备进入氛围模式时位于设置或配置屏幕上,请考虑在应用中显示更相关的屏幕,而不是显示设置视图。
  • 处理设备专用要求
    • 在传递给 onEnterAmbient()AmbientDetails 对象中:
      • 如果 deviceHasLowBitAmbienttrue,请尽可能停用抗锯齿功能。
      • 如果 burnInProtectionRequiredtrue,请定期略微移动界面元素,并避免显示纯白色区域,以防止屏幕烙印。

调试和测试

在开发或测试应用在设备处于氛围模式时的行为时,以下 adb 命令可能很有用:

# put device in ambient mode if the always on display is enabled in settings
# (and not disabled by other settings, such as theatre mode)
$ adb shell input keyevent KEYCODE_SLEEP

# put device in interactive mode
$ adb shell input keyevent KEYCODE_WAKEUP

示例:健身应用

假设有一个锻炼应用,需要在用户的整个锻炼时段向用户显示指标。应用必须在 Ambient 状态转换期间保持可见,并避免被表盘替换。

为此,开发者应执行以下操作:

  1. 实现 AmbientLifecycleObserver 以处理 InteractiveAmbient 状态之间的界面更改,例如调暗屏幕和移除非必需数据。
  2. 按照最佳实践为氛围状态创建新的低功耗布局
  3. 在锻炼期间使用 Ongoing Activity API,以防止系统返回到表盘。

如需了解完整实现,请参阅 GitHub 上基于 Compose 的练习示例。此示例还演示了如何使用 Horologist 库中的 AmbientAware 可组合项来简化 Compose 中的氛围模式处理。