如何开发一个自定义 Complication Provider 来提供健康数据?
解读
Wear OS 的 Complication 相当于表盘上的“小组件”,系统每 1–15 min 主动拉取一次数据,省电且对后台限制极严。国内面试问这道题,核心想验证三点:
- 是否理解 Wear OS 的“Provider 不主动推、由系统拉”的模型;
- 能否把健康数据(如本地传感器、Health Connect、运动 SDK)安全地桥接到 Complication 框架;
- 是否熟悉国内特殊合规要求:后台定位、健康数据属于“个人敏感信息”,必须弹窗授权、加密存储、可撤销,否则应用市场审核直接打回。
因此,回答要围绕“框架接入 + 数据合规 + 性能省电”展开,而不是单纯贴代码。
知识点
- Complication Provider 四大组件:Service 继承
ComplicationDataSourceService,声明android.support.wearable.complications.PROVIDER过滤器;在AndroidManifest.xml中注册并配置update_period_seconds(国内建议 ≥300 s,避免后台频繁唤醒)。 - 数据类型映射:健康场景常用
ComplicationData.TYPE_SHORT_TEXT、TYPE_LONG_TEXT、TYPE_RANGED_VALUE(步数、心率区间)、TYPE_SMALL_IMAGE(心率图标)。 - 权限与合规:
- 传感器:必须动态申请
ACTIVITY_RECOGNITION(Android 10+)与BODY_SENSORS(Android 12+)。 - 后台定位:若需 GPS 轨迹,需额外
ACCESS_BACKGROUND_LOCATION并在应用市场后台“场景说明”里勾选“运动健身”。 - 健康数据:接入 Health Connect 需声明
android.permission.health.READ_STEPS、READ_HEART_RATE,并在首次启动时弹《个人信息收集使用规则》与《健康数据授权说明》,否则华为、小米、OPPO 市场会驳回。
- 传感器:必须动态申请
- 省电策略:
- 使用
ProviderUpdateRequester.requestUpdate()只在用户刚完成一次运动(前台 Service 结束瞬间)时主动触发一次,其余时间完全依赖系统拉取。 - 传感器采样用
SensorManager.SENSOR_DELAY_NORMAL并在onPause()立即unregisterListener(),防止后台 0-uid 挂起。
- 使用
- 数据安全:
- 心率、血氧等原始值只在内存中保留,不落地;需要缓存时写入
EncryptedFile(Tink+Keystore)。 - 提供“一键清除”入口,响应《个人信息保护法》第 49 条“可撤销权”。
- 心率、血氧等原始值只在内存中保留,不落地;需要缓存时写入
- 兼容性:
- Wear 3.x 开始强制 TargetSdk 33,必须适配前台服务
FOREGROUND_SERVICE_TYPE_HEALTH。 - 国内部分手表(如华为 GT 系列)无 GMS,需额外实现华为 Health Kit 桥接,用同一
ComplicationDataSourceService做多 flavor 打包。
- Wear 3.x 开始强制 TargetSdk 33,必须适配前台服务
答案
步骤概览(可直接口述给面试官,展示思路清晰):
-
新建 Wear 模块,依赖:
implementation "androidx.wear.watchface:watchface-complications-data-source:1.2.0" implementation "androidx.health:health-services-client:1.0.0-beta3" -
继承
ComplicationDataSourceService,重写:override fun onComplicationRequest(request: ComplicationRequest, listener: ComplicationRequestListener) { val steps = HealthConnectClient.getSteps(request.lastUpdate) // 伪代码 val data = when (request.complicationType) { ComplicationType.SHORT_TEXT -> ShortTextComplicationData.Builder( PlainComplicationText.Builder("${steps}步").build(), ComplicationText.EMPTY ).build() ComplicationType.RANGED_VALUE -> RangedValueComplicationData.Builder( steps.toFloat(), 0f, 10000f, PlainComplicationText.Builder("${steps}").build() ).build() else -> null } listener.onComplicationData(data) } -
在
AndroidManifest.xml注册:<service android:name=".HealthComplicationService" android:exported="true" android:permission="com.google.android.wearable.permission.BIND_COMPLICATION_PROVIDER"> <intent-filter> <action android:name="android.support.wearable.complications.ACTION_COMPLICATION_UPDATE_REQUEST"/> </intent-filter> <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES" android:value="SHORT_TEXT,RANGED_VALUE"/> <meta-data android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS" android:value="300"/> </service> -
权限与合规:
- 启动页统一动态申请
ACTIVITY_RECOGNITION、BODY_SENSORS,拒绝则跳系统设置。 - 接入 Health Connect 时,先检查
HealthConnectClient.isProviderAvailable(),再调用requestPermissions()弹出 Google 或 OEM 的联合授权页。 - 将健康数据归类为“敏感个人信息”,在应用市场后台上传《隐私政策》与《健康数据使用场景说明》,否则审核必挂。
- 启动页统一动态申请
-
省电与安全:
- 只在用户点击“结束运动”时调用
ProviderUpdateRequester.requestUpdate(context, ComponentName(...))刷新一次;其余时间完全依赖系统 5 min 周期。 - 原始传感器数据不落盘;必须缓存时用
MasterKey+EncryptedFile写入/data/data/<pkg>/files/encrypted/目录,随账号退出即delete()。
- 只在用户点击“结束运动”时调用
-
国内多厂商适配:
- 在
build.gradle定义flavorDimensions "store",含gms与hms两个 flavor;hmsflavor 内用HuaweiHealthKit查询步数,同一套ComplicationDataSourceService做接口隔离,保证 70% 代码复用。
- 在
这样即可在 Wear OS 表盘上稳定、合规地展示步数、心率等健康数据,并通过国内应用市场审核。
拓展思考
- 实时心率曲线:Complication 只允许 1–2 个数据点,若产品坚持“秒级心率曲线”,需引导面试官理解:必须跳转专属 Tile 或 Activity,Complication 本身不适合高频更新;否则会被系统限频甚至拉黑 Provider。
- 无传感器权限的备用方案:可演示如何降级到 Health Connect 的“已同步历史数据”,保证用户拒绝传感器权限时仍显示昨日步数,体现“优雅降级”思维。
- 功耗量化:可补充使用
Battery Historian对比“Provider 周期 300 s”与“30 s”两种场景,证明 300 s 场景下 24 h 传感器耗电 < 1%,给面试官展示数据驱动的优化意识。