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

本指南介绍了如何使应用始终处于运行状态、如何对电源状态转换做出反应,以及如何管理应用行为以在节省电池电量的同时提供良好的用户体验。

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

关键概念

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

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

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

始终开启的应用是指在互动微光状态下均显示内容的应用。

当始终运行的应用在设备处于低功耗氛围状态时继续显示自己的界面时,该应用被描述为处于 ambiactive 模式

系统过渡和默认行为

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

  • 超时时间 1:从互动状态到氛围状态:在用户处于非活动状态一段时间后,设备会进入氛围状态。
  • 超时 #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

作为需要自定义观察方的例外情况,在 Wear OS 6 上,TimeText widget 可感知环境。当设备处于环境状态时,它会自动每分钟更新一次,而无需任何额外的代码。

控制屏幕开启时长

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

防止在持续性活动期间返回到表盘

环境状态(超时 #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 回调,并将这些实时值替换为静态占位内容,例如 --
  • 保持一致的布局
    • 互动微光模式下,让元素保持在同一位置,以实现平滑过渡。
    • 始终显示时间。
  • 了解上下文
    • 如果用户在设备进入环境模式时位于设置或配置界面,请考虑显示应用中更相关的界面,而不是设置视图。
  • 处理设备专用要求
    • 在传递给 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

示例:锻炼应用

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

为实现此目的,开发者应执行以下操作:

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

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