案例研究

WHOOP 如何将过度使用部分唤醒锁定的会话数减少 90% 以上

4 分钟阅读时间

为可穿戴设备构建 Android 应用意味着,当屏幕关闭时,真正的工作才刚刚开始。WHOOP 可帮助会员了解自己的身体对训练、恢复、睡眠和压力的反应,对于许多使用 Android 设备的 WHOOP 会员来说,可靠的后台同步和连接是获得这些洞见的基础。

今年早些时候,Google Play 在 Android Vitals 中发布了一项新指标:过度使用部分唤醒锁定。此指标用于衡量在 24 小时内,累积的非豁免唤醒锁定使用时间超过 2 小时的用户会话所占的百分比。此指标旨在帮助您找出并解决可能导致耗电过快的原因,这对于提供出色的用户体验至关重要。

自 2026 年 3 月 1 日起,如果应用仍未达到质量阈值,可能会被排除在 Google Play 发现界面之外。Google Play 商店商品详情中也可能会显示警告,指出该应用可能会比预期消耗更多电量。

据 WHOOP 的高级 Android 工程师 Mayank Saini 称,在 Android Vitals 将该应用的过度使用部分唤醒锁定百分比标记为 15%(超过建议的 5% 阈值)后,这“为团队提供了一个提高 Android 效率的机会”。

mayank.png

该团队将 Android Vitals 指标视为一个明确的信号,表明他们的后台工作使 CPU 保持唤醒状态的时间过长。解决此问题后,他们便可以继续提供出色的用户体验,同时减少浪费的后台时间,并保持可靠且及时的蓝牙连接和同步。

找出问题

为了确定从何处入手,该团队首先查看了 Android Vitals,以更深入地了解哪些唤醒锁定影响了该指标。通过查阅 Android Vitals 过度使用部分唤醒锁定信息中心,他们能够确定导致过度使用部分唤醒锁定的最大因素是他们的一个 WorkManager worker(在信息中心中标识为 androidx.work.impl.background.systemjob.SystemJobService)。为了支持 WHOOP 的“始终开启体验”,该应用使用 WorkManager 执行后台任务,例如定期同步和向可穿戴设备提供定期更新。

但他们之前并不知道所有后台工作(不只是 WorkManager)的分布情况,直到 Android Vitals 中引入了“过度使用部分唤醒锁定”指标。

但借助信息中心将 WorkManager 确定为主要贡献者,该团队随后能够集中精力确定 哪些工作器贡献最大,并着手解决该问题。

利用内部指标和数据更好地缩小原因范围

**利用内部指标和数据更好地缩小原因范围**WHOOP 已经设置了内部基础架构来监控 WorkManager 指标。

  1. 他们会定期监控:
  2. 平均运行时长:worker 的运行时间有多长?
  3. 超时:worker 超时而不是完成的频率有多高?
  4. 重试:如果工作超时或失败,worker 重试的频率有多高?

取消:工作被取消的频率有多高?

内部指标标记了少数 worker 的 高平均运行时长,使他们能够进一步缩小调查范围。

除了内部指标之外,该团队还使用 Android Studio 的 后台任务检查器来检查和调试相关 worker,并特别关注关联的唤醒锁,以与 Android Vitals 中标记的指标保持一致。

调查:区分 worker 变体

WHOOP 对某些 worker 使用 一次性定期调度。WHOOP 对某些 worker 使用一次性调度和定期调度。 __

这样,应用就可以对具有相同成功条件(仅在时间上有所不同)的相同任务重复使用相同的 worker 逻辑。因此,他们推出了一个更新,使用 WorkManager 的 setTraceTag 方法 来区分同一 Worker 的一次性变体和定期变体。

因此,他们推出了更新,使用 WorkManager 的 _setTraceTag 方法_来区分同一 worker 的一次性变体和定期变体。通过这些额外详细信息,他们可以明确确定哪个 worker 变体(定期或一次性)对过度使用部分唤醒锁定的会话的贡献最大。

WHOOP 的 Android 二级工程师 Manmeet Tuteja 表示:“这种分块也有助于我们确认问题同时出现在这两个变体中,这排除了调度配置的问题,而指向了 worker 实现中共享的业务逻辑问题。”

manmeet.png

深入探讨工作器行为并修复根本原因

这表明问题不是出在调度配置上,而是出在 worker 实现的共享业务逻辑问题上。”**深入了解 worker 行为并解决根本原因**

该团队知道需要查看 worker _内部_的逻辑,因此重新检查了调查期间标记的 worker 的 worker 行为。

具体来说,他们寻找的是工作可能卡住且未完成的实例。 

如果工作开始时没有连接传感器,whoopSensorFlow(表示传感器是否已连接)为 null。**SensorWorker** 未将此视为提前退出条件并继续运行,从而有效地无限期地等待连接。因此,WorkManager 持有部分唤醒锁定,直到工作超时,从而导致后台唤醒锁定使用率高,并频繁、不必要地重新安排 SensorWorker

`SensorWorker` 没有将此视为提前退出的条件,而是继续运行,实际上是无限期地等待连接。

因此,WorkManager 会持有部分唤醒锁定,直到工作超时,从而导致后台唤醒锁定使用率过高,并频繁、不必要地重新安排 `SensorWorker` 的运行。以下代码段展示了解决方案:

class SensorWorker(appContext: Context, params: WorkerParameters): CoroutineWorker(appContext, params) {
   override suspend fun doWork(): Result {
      ...
      // Check the sensor state and perform work or return failure
       return whoopSensorFlow.replayCache
            .firstOrNull()
            ?.let { cachedData ->
                processSensorData(cachedData)
                Result.success()
            } ?: run {
                Result.failure()
            }
}

如果传感器不可用,worker 会退出,从而避免超时情况并释放唤醒锁定。

以下代码段展示了解决方案:

最终,WHOOP 在实施对其 Worker 的更改后仅 30 天,其 过度使用部分唤醒锁定的会话数从 15% 降至不到 1%

partialWake.png

在推出修复程序后,该团队继续监控 Android Vitals 信息中心,以确认更改的影响。

最终,在对 worker 进行更改后仅 30 天,WHOOP 的**过度使用部分唤醒锁定百分比就从 15% 降至不到 1%** 。****

sarthak.png

开始

WHOOP 团队向其他想要提高后台工作效率的开发者提出的建议:

由:
产品营销经理