# Health Connect 转 FHIR

> 从 Fitbit、Garmin、Oura、Withings 和 Wear OS 读取 Android Health Connect 数据，并将其写入 FHIR R4 记录：权限、历史记录、Play 商店审核。

Source: https://developers.anpheros.com/zh/guides/health-connect-fhir

**Health Connect 是 Android 的设备端健康数据存储：Samsung Health、Fitbit、Garmin Connect、Oura、Withings 和 Wear OS 应用将数据写入其中，您的应用在获得用户许可后从中读取数据。** 要将这些数据放入 FHIR 记录，请读取手机上的每日总计和平均值，将它们发送到您的后端，并将每个指标作为 Observation 写入，并附带 LOINC 代码和 UCUM 单位。本页涵盖了 Android 所需的权限、一个 Flutter 示例（已通过 `flutter analyze` 验证），该示例使用相同的代码读取 Android 上的 Health Connect 和 iOS 上的 Apple Health，以及在 Google Play 上发布的规则。

## Health Connect 接收的数据

Health Connect 内置于 Android 14 及更高版本中，在旧版本上也可作为应用使用。它存储着诸如 `HeartRateRecord`、`RestingHeartRateRecord`、`HeartRateVariabilityRmssdRecord`、`OxygenSaturationRecord`、`RespiratoryRateRecord`、`StepsRecord`、`DistanceRecord`、`ActiveCaloriesBurnedRecord`、`SleepSessionRecord`、`WeightRecord`、`BloodPressureRecord`、`BloodGlucoseRecord`、`BodyTemperatureRecord` 和 `ExerciseSessionRecord` 等记录。

根据各供应商的文档，谁会写入数据：

| 源 | 预期内容 |
|---|---|
| Fitbit 和 Pixel Watch | 用户设置后，Fitbit 应用会与 Health Connect 同步（[Google 帮助](https://support.google.com/fitbit/answer/14506680)） |
| Garmin | Garmin Connect 支持 Android 14 及更高版本上的 Health Connect：心率和卡路里等活动和健康数据（[Android Developers，2025 年 8 月](https://developer.android.com/health-and-fitness/community/newsletters/2025/08)） |
| Oura | 心率、HRV、睡眠阶段、步数、距离、卡路里和锻炼数据（[Oura](https://ouraring.com/blog/data-sharing-partner-integrations/)） |
| Withings | Health Mate 通过 Health Connect 交换活动、体重、睡眠和其他数据（[Android Developers 博客](https://android-developers.googleblog.com/2023/03/withings-reduces-data-sync-code-with--health-and-fitness-api-health-connect.html)） |
| Samsung Health, Galaxy Watch | 部分数据，由 Samsung Health 自行决定 — 请参阅 [Samsung Health 到 FHIR](https://developers.anpheros.com/zh/guides/samsung-health-fhir) |

每个供应商应用都必须获得向 Health Connect **写入**数据的权限，该权限由用户在该应用或 Health Connect 的设置中授予。如果缺少某个源的数据，这是首先要检查的事项。

## Android 所需的权限

- 每种记录类型和方向都需要一个权限，例如 `android.permission.health.READ_HEART_RATE` 和 `android.permission.health.READ_STEPS`，在 manifest 中声明并在运行时请求。
- **历史记录：** 如果没有 `android.permission.health.READ_HEALTH_DATA_HISTORY`，应用只能读取授予权限之前的最后 30 天数据。
- **后台：** 在应用不在前台时读取数据需要 `android.permission.health.READ_HEALTH_DATA_IN_BACKGROUND`。
- 一个**权限理由**屏幕（一个处理 `ACTION_SHOW_PERMISSIONS_RATIONALE` 的 Activity，以及 Android 14 上的 Activity 别名），用于解释您的隐私政策；如果没有它，权限对话框将不会打开。
- 在 Flutter 中，`MainActivity` 必须扩展 `FlutterFragmentActivity`，而不是 `FlutterActivity`，否则权限请求将失败。

## 在 Flutter 中读取每日值

[`health`](https://pub.dev/packages/health) 包在 Android 上读取 Health Connect，在 iOS 上读取 Apple Health，统一在一个 API 下。示例使用了 Anpheros Daily 附带的版本 13.2.1：

```dart
import 'dart:math' as math;

import 'package:health/health.dart';

/// One day of data from Health Connect (Android) or Apple Health (iOS), ready for your backend.
class DailyValue {
  DailyValue(this.metric, this.day, this.value, {this.min, this.max});
  final String metric; // 'heart_rate', 'steps'
  final String day; // '2026-10-01', the user's local day
  final double value;
  final double? min, max;

  Map<String, Object?> toJson() => {'metric': metric, 'day': day, 'value': value, 'min': min, 'max': max};
}

final health = Health();

Future<List<DailyValue>> collectLastDays({int days = 30}) async {
  await health.configure();
  if (!await health.isHealthConnectAvailable()) {
    await health.installHealthConnect(); // opens the Play Store on Android; always available on iOS
    return [];
  }
  const types = [HealthDataType.HEART_RATE, HealthDataType.STEPS];
  final granted = await health.requestAuthorization(types, permissions: [HealthDataAccess.READ, HealthDataAccess.READ]);
  if (!granted) return [];

  final now = DateTime.now();
  final out = <DailyValue>[];
  for (var i = days - 1; i >= 0; i--) {
    final start = DateTime(now.year, now.month, now.day - i); // local midnight, safe across DST changes
    final end = DateTime(start.year, start.month, start.day + 1);
    final day = start.toIso8601String().substring(0, 10);

    final steps = await health.getTotalStepsInInterval(start, end);
    if (steps != null) out.add(DailyValue('steps', day, steps.toDouble()));

    final samples = await health.getHealthDataFromTypes(types: [HealthDataType.HEART_RATE], startTime: start, endTime: end);
    final bpm = [for (final p in samples) if (p.value is NumericHealthValue) (p.value as NumericHealthValue).numericValue.toDouble()];
    if (bpm.isNotEmpty) {
      out.add(DailyValue('heart_rate', day, bpm.reduce((a, b) => a + b) / bpm.length,
          min: bpm.reduce(math.min), max: bpm.reduce(math.max)));
    }
  }
  return out; // POST these to your backend, which writes them to the FHIR record
}
```

`getTotalStepsInInterval` 向 Health Connect 请求其聚合总数，这已经根据用户在 Health Connect 中设置的数据源优先级解决了重叠来源（手机和手表计算同一段步行）的问题。自己对原始步数记录求和会导致重复计数。

在 Anpheros Daily 中发布此功能的注意事项：

- **在应用进入前台时同步**，并设置一个简短的保护机制防止重复同步。其他应用会按照自己的计划写入 Health Connect，因此用户在其供应商应用中看到的数据可能需要几分钟或几小时后才会到达。
- **心率是一个序列**，而不是每个样本一个值：在手机上进行汇总（每日，或用于图表的 4 小时窗口）而不是上传每次读取。
- **这里的 HRV 是 RMSSD**。请勿将其存储在用于 Apple Health 的 SDNN 代码下。

## 从 Flutter 值到 FHIR Observation

将值发送到您的后端，并为每个人、每项指标和每天写入一个 Observation，并附带一个稳定的标识符，当同一天的数据再次发送时进行更新。完整的映射表（Health Connect 记录 → LOINC → UCUM）、一个经过验证的 FHIR 示例以及在平台上运行的 TypeScript upsert 命令，都可以在 [Wearable data in FHIR](https://developers.anpheros.com/zh/guides/wearable-data-fhir) 中找到。

## 在 Google Play 上发布

读取 Health Connect 的应用必须在 Play Console 中完成 Health Connect 权限声明，并为每种数据类型提供理由，同时提供与之匹配的隐私政策。应用中未明显使用的所有数据类型请求都将被拒绝。请规划审核流程：在每个使用它的屏幕旁边列出相应的数据类型。

## Wear OS

在 Wear OS 手表上，应用可以通过 Health Services 读取实时传感器数据（例如，运动期间的心率）。手表自身健康应用记录的数据会通过供应商的手机应用传输到手机上的 Health Connect。对于病历记录，通常读取手机上的 Health Connect 就足够了；手表应用主要用于实时功能。

## Anpheros 的定位

Anpheros 平台存储结果：个人的 FHIR R4 记录，其中每个可穿戴设备的值都作为一个 Observation 标记为设备数据，并受到同意、来源和访问日志的保护。它本身不读取 Health Connect；您的 Android 应用会读取。Anpheros 自有的应用 **Anpheros Daily** 会读取 Android 上的 Health Connect（适用于 Plus 和 Max 套餐），并将每日摘要写入记录。

## 常见问题解答

### Health Connect 是否提供云 API？
否。Health Connect 将数据存储在手机上，只有该手机上的应用在获得用户许可后才能读取。要将数据传输到服务器，您的应用需要读取数据并将其发送出去。

### Health Connect 是否提供 REST API 或 Web API？
否。Health Connect 是一个设备上的 Android API，没有 REST 或 Web 端点。只有当手机上的应用读取数据并将其发送出去时，服务器才能接收到数据，例如将 FHIR Observations 发送到 Anpheros。

### 我能从 Python 或 React Native 中使用 Health Connect API 吗？
不能从 Python 中使用，因为它运行在手机上，而不是服务器上。从 React Native 或 Flutter 中可以，通过调用应用内部 Android API 的社区或官方插件来实现。

### 为什么我的应用只能读取 30 天的 Health Connect 数据？
如果没有 `READ_HEALTH_DATA_HISTORY` 权限，Health Connect 会将读取限制在授予权限之前的 30 天内。如果您需要更早的数据，请请求历史记录权限。

### Garmin 是否与 Health Connect 同步？
是。Google 于 2025 年 8 月宣布，Garmin Connect 支持 Android 14 及更高版本上的 Health Connect，用于活动和健康数据，如心率和卡路里。Garmin 自有的评分（如 Body Battery）不共享。

### 我能否在不使用 Fitbit Web API 的情况下读取 Fitbit 数据？
在 Android 上可以：一旦用户在 Fitbit 应用中启用 Health Connect，Fitbit 数据就会在 Health Connect 中提供给他们授权的应用。

### 为什么 Health Connect 的步数统计与我的供应商应用不同？
供应商应用可能尚未导出其最新数据，可能只导出部分类型，或者 Health Connect 可能更偏好同一时期的其他数据源。请在 Health Connect 的设置中检查数据源及其优先级。

## 相关资源

- [Fitbit Web API 停用（2026 年 10 月）](https://developers.anpheros.com/zh/guides/fitbit-web-api-shutdown)
- [FHIR 中的可穿戴设备数据](https://developers.anpheros.com/zh/guides/wearable-data-fhir)：映射表和后端代码
- [Samsung Health 转 FHIR](https://developers.anpheros.com/zh/guides/samsung-health-fhir)
- [Apple Health (HealthKit) 转 FHIR](https://developers.anpheros.com/zh/guides/apple-health-fhir)
- [如何使用 Flutter 构建医疗保健应用](https://developers.anpheros.com/guides/healthcare-app-flutter)
