使用HealthConnect API时卡路里数据聚合结果存在差异
Health Connect API 手动求和与聚合结果不一致的解决方案
问题背景
通过Kotlin实现校验逻辑,对比过去30天内每日手动汇总的TotalCaloriesBurnedRecord卡路里值,与Health Connect aggregate接口返回的ENERGY_TOTAL聚合值,结果始终存在巨大差异。日志显示手动求和远小于聚合值,使用官方测试工具也复现了该问题。
核心原因
代码中仅调用了一次readRecords接口,未处理分页返回逻辑。当每日的卡路里记录数量超过设置的pageSize(2000)时,API会将数据分页返回,仅获取第一页记录会导致手动求和遗漏后续页的内容(比如系统自动生成的基础代谢记录、其他应用写入的批量小记录),而aggregate接口会计算所有记录的总和,因此出现数值差异。
解决方案
循环调用readRecords,直到返回的nextPageToken为null,收集所有分页的记录后再进行求和计算。
修正后的代码
suspend fun validateCaloriesData(healthConnectClient: HealthConnectClient) { // Time zone and date setup val zoneId = ZoneId.systemDefault() val endTime = Instant.now().atZone(zoneId).truncatedTo(ChronoUnit.DAYS) val startTime = endTime.minusDays(30) var currentDay = startTime while (currentDay.isBefore(endTime)) { val startOfDay = currentDay.toInstant() val endOfDay = currentDay.plusDays(1).toInstant() val dailyRecords = mutableListOf<Double>() try { var pageToken: String? = null // 循环读取所有分页的记录 do { val records = healthConnectClient.readRecords( ReadRecordsRequest( recordType = TotalCaloriesBurnedRecord::class, timeRangeFilter = TimeRangeFilter.between(startOfDay, endOfDay), pageSize = 2000, pageToken = pageToken, ascendingOrder = true ) ) records.records.forEach { dailyRecords.add(it.energy.inKilocalories ?: 0.0) } pageToken = records.nextPageToken } while (pageToken != null) val sumOfDailyRecords = dailyRecords.sum() val response = healthConnectClient.aggregate( AggregateRequest( metrics = setOf(TotalCaloriesBurnedRecord.ENERGY_TOTAL), timeRangeFilter = TimeRangeFilter.between(startOfDay, endOfDay) ) ) val aggregatedCalories = response[TotalCaloriesBurnedRecord.ENERGY_TOTAL]?.inKilocalories ?: 0.0 // 考虑浮点数精度问题,使用近似对比 if (kotlin.math.abs(sumOfDailyRecords - aggregatedCalories) > 0.01) { Log.e("DailyCaloriesValidation", "Discrepancy found on day $currentDay: Manual sum is $sumOfDailyRecords but aggregated is $aggregatedCalories") } else { Log.i("DailyCaloriesValidation", "Match on day $currentDay: $sumOfDailyRecords") } } catch (e: Exception) { Log.e("DailyCaloriesValidation", "Error reading or aggregating calories for day $currentDay: ${e.message}") } currentDay = currentDay.plusDays(1) } }
额外说明
- 浮点数精度处理:由于卡路里数值可能存在小数位,直接用
!=对比容易出现精度误差,改用绝对值差小于0.01的近似对比更合理。 - 记录完整性:确保应用已申请
READ_TOTAL_CALORIES_BURNED权限,否则无法读取所有来源的卡路里记录。
内容的提问来源于stack exchange,提问作者Catfoxes
相关产品推荐
相关产品推荐

