You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Watch Face Service中正确获取心率BPM数据?

问题描述

我用Jetpack Watch Face API制作了一款表盘,希望将心率BPM数据转为字符串绘制到画布上。不想使用complication(因其未来可能被弃用,且仅适用于Watch Face Format API),但不确定应直接从传感器还是通过Health Service API获取数据。

尝试了多种方法后选择使用Health Service API的被动监测方案,但该方案无法正常工作:模拟器中设置的BPM数据无法更新,且onNewDataPointsReceived函数从未被调用,Logcat中也未出现任何错误。想知道实现是否存在问题,以及获取这类数据的正确方式。

简化后的代码如下:

class RisingSunCanvasRenderer( /* nothing fancy here */) : Renderer.CanvasRenderer2<RisingSunCanvasRenderer.CSharedAssets>(/* nothing fancy here */) {
 private val healthClient = HealthServices.getClient(context)
    private val passiveMonitoringClient = healthClient.passiveMonitoringClient
    object HeartRate {
        private var bpm = 0.0f;

        fun setBpm(newBpm: Float) {
            bpm = newBpm
        }
        fun getBpm(): Float {
            return bpm
        }
    }

    class PassiveDataService : PassiveListenerService() { // Listener Service
        override fun onNewDataPointsReceived(dataPoints: DataPointContainer) {
            val data = dataPoints.getData(DataType.HEART_RATE_BPM)
            HeartRate.setBpm(data.last().value.toFloat())

            Log.d("HEARTBEAT", "value updated")
        }
    }

    init { // I'm creating the listener on init of the renderer
        val passiveListenerConfig = PassiveListenerConfig.builder()
            .setDataTypes(setOf(DataType.HEART_RATE_BPM))
            .build()

        passiveMonitoringClient.setPassiveListenerServiceAsync(
            PassiveDataService::class.java,
            passiveListenerConfig
        )

        Log.d("MONITORING", "Init monitoring completed")
    }

    override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime, sharedAssets: CSharedAssets) {
        canvas.drawColor(Color.BLACK)
        canvas.drawText(HeartRate.getBpm().toString(), bounds.centerX().toFloat(), bounds.centerY() + 100f, paint)
    }
}
问题分析与修复方案

1. PassiveListenerService的类声明错误

你将PassiveDataService定义为RisingSunCanvasRenderer的内部类,系统无法单独实例化这个Service(内部类依赖外部类实例)。

  • 修复:将PassiveDataService改为顶层类,或添加companion object修饰使其成为静态内部类:
    // 改为顶层类
    class PassiveDataService : PassiveListenerService() {
        override fun onNewDataPointsReceived(dataPoints: DataPointContainer) {
            val data = dataPoints.getData(DataType.HEART_RATE_BPM)
            if (data.isNotEmpty()) {
                HeartRate.setBpm(data.last().value.toFloat())
                Log.d("HEARTBEAT", "value updated")
            }
        }
    }
    
    同时必须在AndroidManifest.xml中注册该Service:
    <service
        android:name=".PassiveDataService"
        android:exported="false">
        <intent-filter>
            <action android:name="com.google.android.gms.health.PASSIVE_LISTENER"/>
        </intent-filter>
    </service>
    

2. 缺失权限配置

获取心率数据需要声明并申请危险权限:

  • 在AndroidManifest.xml中添加权限:
    <uses-permission android:name="android.permission.health.READ_HEART_RATE"/>
    
  • 在运行时请求权限,建议在表盘的配置Activity或WatchFaceService的onCreate中处理:
    // 在WatchFaceService中示例
    private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
        if (granted) {
            // 权限通过后初始化被动监测
            initPassiveMonitoring()
        }
    }
    
    override fun onCreate() {
        super.onCreate()
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_HEART_RATE) != PackageManager.PERMISSION_GRANTED) {
            requestPermissionLauncher.launch(Manifest.permission.READ_HEART_RATE)
        } else {
            initPassiveMonitoring()
        }
    }
    
    private fun initPassiveMonitoring() {
        val healthClient = HealthServices.getClient(this)
        val passiveMonitoringClient = healthClient.passiveMonitoringClient
        val config = PassiveListenerConfig.builder()
            .setDataTypes(setOf(DataType.HEART_RATE_BPM))
            .build()
        passiveMonitoringClient.setPassiveListenerServiceAsync(PassiveDataService::class.java, config)
    }
    

3. 初始化时机错误

你在RisingSunCanvasRenderer的init块中初始化监测,但此时权限可能还未获取,导致配置无效。

  • 修复:将初始化逻辑移到权限请求成功之后,如上述initPassiveMonitoring方法,确保只有权限通过才启动监测。

4. 模拟器数据模拟方式错误

在Wear模拟器中,需通过Extended Controls > Sensors面板发送心率数据,而非其他设置入口。确保在此处触发BPM更新,否则被动监测不会收到数据。

5. 线程安全问题

HeartRate单例的bpm变量在多线程环境下可能存在同步问题,需添加线程安全保障:

object HeartRate {
    @Volatile private var bpm = 0.0f
    fun setBpm(newBpm: Float) { bpm = newBpm }
    fun getBpm(): Float { return bpm }
}

正确流程总结

  1. 声明并申请READ_HEART_RATE权限。
  2. 将PassiveDataService改为顶层类并在Manifest中注册。
  3. 权限通过后初始化被动监测。
  4. 在onNewDataPointsReceived中处理心率数据,更新线程安全的全局变量。
  5. 在render方法中读取心率值并绘制到画布。

内容的提问来源于stack exchange,提问作者R3DC0DE

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 13:35:31