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

如何过滤Health Connect中来自Google Fit的手动录入步数?

如何通过Health Connect区分自动记录与Google Fit手动录入的步数?

我通过Health Connect实现了获取用户当日步数的功能,但用户通过Google Fit手动录入的大量不符合实际的步数(同步至Health Connect)导致功能异常。我尝试通过record.metadata.recordingMethod字段过滤自动记录的步数,但发现该字段始终返回未知值0,无论是手动还是自动记录的条目都是如此:

手动录入的步数记录:

StepsRecord(startTime=2025-02-04T10:24:12.681Z, startZoneOffset=null, endTime=2025-02-04T10:54:12.682Z, endZoneOffset=null, count=2500, metadata=Metadata(id='c9196ccf-9fa5-4557-9651-7cdcb953ebeb', dataOrigin=DataOrigin(packageName='com.google.android.apps.fitness'), lastModifiedTime=2025-02-04T10:54:34.455Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))

自动记录的步数记录:

StepsRecord(startTime=2025-02-04T02:13:42.690Z, startZoneOffset=null, endTime=2025-02-04T02:14:42.691Z, endZoneOffset=null, count=20, metadata=Metadata(id='9752d916-b8f3-4c21-9a19-0e9471098346', dataOrigin=DataOrigin(packageName='com.google.android.apps.fitness'), lastModifiedTime=2025-02-04T02:31:05.696Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))

我的过滤代码如下:

suspend fun getTodayTotalSteps(healthConnectClient: HealthConnectClient): Long {
    Timber.d("counting_to")
    val now = LocalDateTime.now().with(LocalTime.MAX)
    val startOfDay = now.toLocalDate().atStartOfDay()
    var totalSteps = 0L
    try {
        Timber.d("start_time:::$startOfDay")
        Timber.d("end_time:::$now")
        val response =
            healthConnectClient.readRecords(
                ReadRecordsRequest(
                    StepsRecord::class,
                    timeRangeFilter = TimeRangeFilter.between(startOfDay, now)
                )
            )

        for (record in response.records) {
            if (record.metadata.recordingMethod == RECORDING_METHOD_AUTOMATICALLY_RECORDED) {
                totalSteps += record.count
            }
            Timber.d("total_steps_after_Wallet_creation:::$totalSteps")
        }
        return totalSteps

    } catch (e: Exception) {
        Timber.d("error in getting steps today $e")
    }
    return totalSteps
}

请问如何准确区分自动记录与手动录入的步数?


解决方案

问题出在Google Fit同步至Health Connect时,并未正确填充recordingMethod字段,导致该字段无法用于区分记录类型。可以通过以下替代方案解决:

1. 利用Google Fit手动记录的clientRecordId特征

部分场景下,Google Fit手动录入的步数记录会带有manual:<UUID>格式的clientRecordId,可以通过判断该前缀过滤手动记录:

for (record in response.records) {
    // 仅保留非手动录入的记录
    if (record.metadata.clientRecordId?.startsWith("manual:") != true) {
        totalSteps += record.count
    }
}

注意:部分情况下手动记录的clientRecordId可能为null,需要结合其他特征验证。

2. 基于步速的启发式过滤

手动录入的步数通常是一次性大数值,步速远超正常运动范围(正常步行约每分钟60-120步,跑步约120-180步)。可以计算单条记录的步速,过滤异常值:

for (record in response.records) {
    val durationMinutes = Duration.between(record.startTime, record.endTime).toMinutes()
    if (durationMinutes <= 0) {
        // 时长为0的记录大概率是手动录入,直接跳过
        continue
    }
    val stepsPerMinute = record.count / durationMinutes
    // 过滤步速超过200步/分钟的异常记录
    if (stepsPerMinute <= 200) {
        totalSteps += record.count
    }
}

这种方法虽非100%准确,但能过滤绝大多数明显的虚假手动步数。

3. 直接集成Google Fit API(精准区分)

如果需要完全准确的区分,可以绕过Health Connect,直接调用Google Fit API。手动录入的步数数据源通常标记为user_input,可以通过数据源特征过滤:

// 初始化Google Fit客户端(需提前配置OAuth权限)
val fitnessOptions = FitnessOptions.builder()
    .addDataType(DataType.TYPE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
    .build()

val account = GoogleSignIn.getAccountForExtension(context, fitnessOptions)
Fitness.getHistoryClient(context, account)
    .readData(
        DataReadRequest.Builder()
            .read(DataType.TYPE_STEP_COUNT_DELTA)
            .setTimeRange(startOfDay.toInstant(ZoneOffset.UTC), now.toInstant(ZoneOffset.UTC), TimeUnit.MILLISECONDS)
            .build()
    )
    .addOnSuccessListener { result ->
        var totalAutoSteps = 0L
        for (dataSet in result.dataSets) {
            val dataSource = dataSet.dataSource
            // 过滤手动录入的数据源
            if (!dataSource.streamName.contains("user_input")) {
                for (dataPoint in dataSet.dataPoints) {
                    totalAutoSteps += dataPoint.getValue(Field.FIELD_STEPS).asInt()
                }
            }
        }
        // 使用totalAutoSteps
    }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:05:57