如何过滤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
相关产品推荐
相关产品推荐

