3. 表盘与复杂功能生产实践

表盘是 Wear OS 上最容易被用户长期驻留的界面,也是功耗最敏感的界面之一。传统做法把表盘写成可执行 APK,通过 Canvas 或 OpenGL 自行绘制,灵活但难以约束耗电与内存。Watch Face Format(WFF)改变了这一点:它把表盘描述为一份 XML 资源包,由系统表盘引擎负责渲染与刷新调度,开发者不再持有绘制循环,而是声明“显示什么、何时变化、数据从哪来”。理解 WFF 的结构、复杂功能数据源以及刷新与功耗的取舍,是表盘能否通过审核并长期稳定运行的关键。

一、Watch Face Format 的组成与运行方式

一个 WFF 表盘本质上是一个只含资源的 APK,核心是 res/raw/watchface.xml,配合图片、字体与 AndroidManifest.xml 中的 com.google.android.wearable.watchface.category.WATCH_FACE 声明。XML 由若干层组成:WatchFace 根节点定义元数据与场景;Scene 描述一组可切换的显示状态(如环境模式与交互模式);Group 与 PartDraw 负责布局与绘制;PartText、PartImage 承载具体内容;Variant 用于按条件切换样式。

与命令式绘制最大的区别在于:WFF 中所有随时间变化的内容都必须通过表达式声明,例如 [HOUR_0_23]、[MINUTE]、[SECOND],或复杂功能数据 [COMPLICATION.STEP_COUNT]。系统据此决定何时重绘。开发者不能写“每秒执行一次”的代码,只能声明“这个文本依赖秒”,刷新频率由引擎按场景与功耗策略决定。这一约束正是 WFF 能控制功耗的根本原因。

WFF 表盘:声明式结构 → 数据源 → 刷新与功耗 watchface.xml 结构 WatchFace(元数据) Scene(交互 / 环境) Group / PartDraw(布局) PartText / PartImage Variant(条件样式) 表达式声明变化源 [HOUR] [MINUTE] [SECOND] Complication 数据源 SHORT_TEXT / LONG_TEXT 步数、心率、天气 RANGED_VALUE 进度弧、电量环 MONOCHROMATIC_ICON 图标型数据 EMPTY / NOT_CONFIGURED 未授权或未选择 数据更新由系统推送 表盘不主动轮询 按 provider 更新频率到达 刷新策略与功耗 交互模式 秒针可动,约 1 Hz 重绘 环境模式(AOD) 仅分钟级变化,低亮度 屏幕熄灭 停止重绘,仅保留状态 功耗红线 AOD 下禁止秒级动画 避免大面积高亮像素 减少每秒变化的图层数 表达式声明依赖 → 系统按场景调度刷新 → 数据源变化触发局部重绘 → 功耗由刷新频率与像素面积共同决定

二、Complication 数据源:让表盘“接上”真实数据

复杂功能(Complication)是表盘显示外部数据的标准通道。WFF 表盘不直接读取传感器,而是声明自己需要哪类数据槽位,由系统把用户选择的 provider 数据填入。常见类型包括 SHORT_TEXT(短文本,如步数、温度)、LONG_TEXT(长文本,如日程标题)、RANGED_VALUE(带范围的数值,适合画进度弧)、MONOCHROMATIC_ICON(单色图标)以及 EMPTY、NOT_CONFIGURED 等占位状态。

在 XML 中,一个数据槽位通常这样声明:

<ComplicationSlot slotId="1">
  <DefaultProviderPolicy
      defaultSystemProvider="STEP_COUNT"
      defaultSystemProviderType="SHORT_TEXT"/>
  <Complication type="SHORT_TEXT">
    <PartText>
      <Text><![CDATA[[COMPLICATION.SHORT_TEXT]]]></Text>
    </PartText>
  </Complication>
</ComplicationSlot>

这里的关键是 DefaultProviderPolicy:当用户尚未选择数据源时,系统用默认 provider 填充,避免出现空白。生产实践中必须处理三种边界状态:NOT_CONFIGURED(用户未选)、EMPTY(provider 无数据)、以及数据到达延迟。推荐做法是为每种状态准备独立的 Variant,例如未配置时显示“--”或引导图标,而不是让文本节点消失导致布局跳动。

数据更新频率由 provider 决定,不由表盘控制。步数通常按分钟级推送,心率在运动场景下更频繁,天气可能十几分钟一次。表盘不应假设数据“每秒都新”,也不应在表达式中对同一数据做高频重复计算。若需要展示“数据更新时间”,应使用 provider 提供的时间戳字段,而不是自己维护计时器。

三、实时刷新与功耗的平衡

WFF 的刷新由系统按场景调度,但开发者仍可通过声明方式显著影响功耗。核心原则是:只让真正需要变化的内容依赖高频表达式。例如秒针依赖 [SECOND],在交互模式下每秒重绘一次;而日期、步数、电量只依赖分钟级或数据源变化,不应与秒针放在同一高频图层组中。若把整个表盘包在一个依赖秒的 Group 里,系统会认为整屏每秒都在变,功耗随之上升。

环境模式(AOD)是功耗红线最集中的场景。此时屏幕以低亮度、低刷新率显示,通常只允许分钟级变化。生产要求包括:AOD 场景中不出现秒针或秒级动画;不使用大面积纯白或高亮像素;减少每秒变化的图层数量;对 OLED 屏幕尽量让背景接近黑色以降低发光功耗。若表盘在 AOD 下仍声明秒级依赖,轻则被系统降级,重则无法通过表盘审核。

一个可操作的检查方法是:在交互模式与环境模式分别统计“依赖秒级表达式的节点数”和“每秒重绘的像素面积占比”。示例目标可设为:交互模式下秒级节点不超过 3 个、重绘面积不超过屏幕的 15%;AOD 下秒级节点为 0、重绘面积接近 0。这些数值属于合理假设的工程参考,实际应以真机功耗曲线为准。

调试建议:使用 adb shell dumpsys SurfaceFlinger 观察表盘图层的合成频率,结合 adb shell dumpsys batterystats 对比不同刷新策略下的耗电差异。修改 XML 后重新安装表盘 APK,观察 AOD 切换时是否出现闪烁或数据错位。

四、生产实践中的常见问题

  • 数据槽位空白:未处理 NOT_CONFIGURED,用户首次使用时看到空位。应提供默认 provider 与占位 Variant。
  • AOD 闪烁:交互模式与环境模式使用了不同的布局尺寸或字体,切换时发生跳变。应保持两套 Scene 的几何结构一致,仅调整颜色与可见性。
  • 功耗超标:把秒级表达式误用于整个 Group,或 AOD 下保留动画。应拆分图层,按变化频率分组。
  • 审核被拒:缺少默认 provider、AOD 下仍有秒针、或使用了 WFF 不支持的表达式。提交前应逐条核对官方兼容性清单。

把表盘当作“声明式数据视图”而非“自绘程序”,是 WFF 生产实践的核心心态转变。结构上分清 Scene 与 Group,数据上接好 Complication 槽位并处理边界状态,刷新上按变化频率拆分图层、守住 AOD 红线,表盘才能在真实设备上既好看又省电,并顺利通过审核与长期运行。