Чтение агрегированных данных

Агрегирование данных в Здоровье и спорте включает базовое агрегирование или агрегирование данных в сегменты. Ниже описано, как это сделать.

Базовое агрегирование

Чтобы выполнить базовое агрегирование данных, используйте функцию aggregate для объекта HealthConnectClient. Он принимает объект AggregateRequest, в котором вы добавляете типы показателей и диапазон времени в качестве параметров. Название базовых агрегированных данных зависит от типа используемых показателей.

Совокупное агрегирование

При суммарном агрегировании вычисляется общее значение.

В примере ниже показано, как агрегировать данные для типа данных:

suspend fun readDistanceAggregate(startTime: Instant, endTime: Instant): Number {
    val response = healthConnectClient.aggregate(
        AggregateRequest(
            metrics = setOf(DistanceRecord.DISTANCE_TOTAL),
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
        )
    )
    return response[DistanceRecord.DISTANCE_TOTAL]?.inMeters ?: 0L
}

Как фильтровать данные по источнику

Вы также можете фильтровать агрегированные данные по их источнику. Например, можно включить только данные, записанные определенным приложением.

В следующем примере показано, как использовать dataOriginFilter и AggregateRequest для агрегирования данных о шагах из определенного приложения:

suspend fun aggregateStepsFromSpecificApp(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant,
    appPackageName: String
) {
    try {
        val response = healthConnectClient.aggregate(
            AggregateRequest(
                metrics = setOf(StepsRecord.COUNT_TOTAL),
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
                dataOriginFilter = setOf(DataOrigin(appPackageName))
            )
        )
        // The result may be null if no data is available in the time range
        val totalSteps = response[StepsRecord.COUNT_TOTAL] ?: 0L
    } catch (e: Exception) {
        // Run error handling here
    }
}

Статистическое агрегирование

Статистическое агрегирование позволяет вычислить минимальное, максимальное или среднее значение записей с образцами.

Ниже приведен пример использования статистической агрегации.

suspend fun readHeartRateAggregate(startTime: Instant, endTime: Instant): Pair<Long, Long> {
    val response = healthConnectClient.aggregate(
        AggregateRequest(
            metrics = setOf(HeartRateRecord.BPM_MAX, HeartRateRecord.BPM_MIN),
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
        )
    )
    val minimumHeartRate = response[HeartRateRecord.BPM_MIN] ?: 0L
    val maximumHeartRate = response[HeartRateRecord.BPM_MAX] ?: 0L

    return maximumHeartRate to minimumHeartRate
}

Сегменты

Платформа "Здоровье и спорт" также позволяет агрегировать данные в группы. Вы можете использовать два типа сегментов: продолжительность и период.

После вызова они возвращают список сегментов. Обратите внимание, что список может быть неполным, поскольку в нем не показываются сегменты, не содержащие данных.

Продолжительность

В этом случае агрегированные данные разбиваются на сегменты с фиксированной продолжительностью, например в одну минуту или один час. Чтобы агрегировать данные в сегменты, используйте aggregateGroupByDuration. Он принимает объект AggregateGroupByDurationRequest, в котором вы добавляете типы показателей, временной диапазон и Duration в качестве параметров. Для свойств startTime и endTime в объекте TimeRangeFilter можно использовать пары объектов Instant или LocalDateTime.

Ниже приведен пример агрегирования шагов в интервалы длительностью в одну минуту.

suspend fun aggregateStepsIntoMinutes(
    healthConnectClient: HealthConnectClient,
    startTime: LocalDateTime,
    endTime: LocalDateTime
) {
    try {
        val response =
            healthConnectClient.aggregateGroupByDuration(
                AggregateGroupByDurationRequest(
                    metrics = setOf(StepsRecord.COUNT_TOTAL),
                    timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
                    timeRangeSlicer = Duration.ofMinutes(1L)
                )
            )
        for (durationResult in response) {
            // The result may be null if no data is available in the time range
            val totalSteps = durationResult.result[StepsRecord.COUNT_TOTAL] ?: 0L
        }
    } catch (e: Exception) {
        // Run error handling here
    }
}

Период

В этом случае агрегированные данные разбиваются на сегменты в пределах временного интервала, например недели или месяца. Чтобы агрегировать данные в сегменты, используйте aggregateGroupByPeriod. Он принимает объект AggregateGroupByPeriodRequest, в котором вы добавляете типы показателей, временной диапазон и Period в качестве параметров.

Ниже приведен пример агрегирования шагов в месячные интервалы.

suspend fun aggregateStepsIntoMonths(
    healthConnectClient: HealthConnectClient,
    startTime: LocalDateTime,
    endTime: LocalDateTime
) {
    try {
        val response =
            healthConnectClient.aggregateGroupByPeriod(
                AggregateGroupByPeriodRequest(
                    metrics = setOf(StepsRecord.COUNT_TOTAL),
                    timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
                    timeRangeSlicer = Period.ofMonths(1)
                )
            )
        for (monthlyResult in response) {
            // The result may be null if no data is available in the time range
            val totalSteps = monthlyResult.result[StepsRecord.COUNT_TOTAL] ?: 0L
        }
    } catch (e: Exception) {
        // Run error handling here
    }
}

Ограничения на чтение

По умолчанию все приложения могут читать данные из Health Connect за последние 30 дней до того, как было предоставлено разрешение.

Если вам нужно предоставить разрешения на чтение, которые выходят за рамки ограничений по умолчанию, запросите разрешение PERMISSION_READ_HEALTH_DATA_HISTORY. В противном случае при попытке прочитать записи старше 30 дней возникнет ошибка.

История разрешений для удаленного приложения

Если пользователь удалит ваше приложение, все разрешения, в том числе разрешение на доступ к истории, будут отозваны. Если пользователь переустановит ваше приложение и снова предоставит ему разрешение, будут действовать те же ограничения по умолчанию, и ваше приложение сможет считывать данные из сервиса "Здоровье и спорт" за период до 30 дней до новой даты.

Предположим, пользователь удалил ваше приложение 10 мая 2023 г., а затем установил его снова 15 мая 2023 г. и предоставил разрешение на чтение. Теперь по умолчанию приложение может считывать данные начиная с 15 апреля 2023 г..

Агрегированные данные, на которые влияют приоритеты приложений, выбранные пользователем

Конечные пользователи могут задавать приоритет для приложений, связанных с отслеживанием сна и активности через платформу "Здоровье и спорт". Изменять эти списки приоритетов могут только конечные пользователи. При чтении агрегированных данных Aggregate API учитывает все дубликаты и сохраняет только данные из приложения с наивысшим приоритетом. Дублирование данных может происходить, если пользователь одновременно использует несколько приложений, записывающих данные одного типа, например количество шагов или пройденное расстояние.

Изображение с информацией о том, как изменить приоритет приложений
Рисунок 1. Изменение порядка приоритета приложений

Изображение, на котором показано, как изменить приоритет приложений

Информацию о том, как конечные пользователи могут задавать приоритет для приложений, можно найти в разделе Как управлять данными в приложении "Здоровье и спорт".

Пользователь может добавлять и удалять приложения, а также менять их приоритет. Пользователь может удалить приложение, которое записывает дублирующиеся данные, чтобы суммарные показатели на экране "Здоровья и спорта" совпадали с данными в приложении, которому он отдал наивысший приоритет. Общее количество данных обновляется в реальном времени.

Несмотря на то что Aggregate API рассчитывает данные приложений "Активность" и "Сон", удаляя дубликаты в соответствии с приоритетами, заданными пользователем, вы можете создать собственную логику для отдельного расчета данных для каждого приложения, записывающего эти данные.

Сервис "Здоровье и спорт" удаляет дубликаты только для типов данных "Активность" и "Данные о сне". Итоговые значения, которые показываются в приложении, – это результаты дедупликации, выполненной с помощью Aggregate API. В этих значениях указаны данные за последний полный день, когда были доступны данные о шагах и расстоянии. Для других типов данных агрегированные результаты объединяют все данные этого типа в сервисе "Здоровье и спорт" из всех приложений, которые их записали.

Чтение в фоновом режиме

Вы можете запросить разрешение на то, чтобы ваше приложение работало в фоновом режиме и считывало данные с платформы "Здоровье и спорт". Если вы запросите разрешение Чтение в фоновом режиме, пользователь сможет предоставить приложению доступ к чтению данных в фоновом режиме.

Поддерживаемые агрегированные типы данных по записям

В таблице ниже перечислены все поддерживаемые агрегированные типы данных по записям Здоровья и спорта.

Таблица: поддерживаемые типы агрегированных данных по записям
Запись Тип агрегированных данных
ActiveCaloriesBurnedRecord ACTIVE_CALORIES_TOTAL
ActivityIntensityRecord DURATION_TOTAL, INTENSITY_MINUTES_TOTAL, MODERATE_DURATION_TOTAL, VIGOROUS_DURATION_TOTAL
BasalMetabolicRateRecord BASAL_CALORIES_TOTAL
BloodPressureRecord DIASTOLIC_AVG, DIASTOLIC_MAX, DIASTOLIC_MIN, SYSTOLIC_AVG, SYSTOLIC_MAX, SYSTOLIC_MIN
CyclingPedalingCadenceRecord RPM_AVG, RPM_MAX, RPM_MIN
DistanceRecord DISTANCE_TOTAL
ElevationGainedRecord ELEVATION_GAINED_TOTAL
ExerciseSessionRecord EXERCISE_DURATION_TOTAL
FloorsClimbedRecord FLOORS_CLIMBED_TOTAL
HeartRateRecord BPM_AVG, BPM_MAX, BPM_MIN, MEASUREMENTS_COUNT
HeightRecord HEIGHT_AVG, HEIGHT_MAX, HEIGHT_MIN
HydrationRecord VOLUME_TOTAL
MindfulnessSessionRecord MINDFULNESS_DURATION_TOTAL
NutritionRecord BIOTIN_TOTAL, CAFFEINE_TOTAL, CALCIUM_TOTAL, CHLORIDE_TOTAL, CHOLESTEROL_TOTAL, CHROMIUM_TOTAL, COPPER_TOTAL, DIETARY_FIBER_TOTAL, ENERGY_FROM_FAT_TOTAL, ENERGY_TOTAL, FOLATE_TOTAL, FOLIC_ACID_TOTAL, IODINE_TOTAL, IRON_TOTAL, MAGNESIUM_TOTAL, MANGANESE_TOTAL, MOLYBDENUM_TOTAL, MONOUNSATURATED_FAT_TOTAL, NIACIN_TOTAL, PANTOTHENIC_ACID_TOTAL, PHOSPHORUS_TOTAL, POLYUNSATURATED_FAT_TOTAL, POTASSIUM_TOTAL, PROTEIN_TOTAL, RIBOFLAVIN_TOTAL, SATURATED_FAT_TOTAL, SELENIUM_TOTAL, SODIUM_TOTAL, SUGAR_TOTAL, THIAMIN_TOTAL, TOTAL_CARBOHYDRATE_TOTAL, TOTAL_FAT_TOTAL, TRANS_FAT_TOTAL, UNSATURATED_FAT_TOTAL, VITAMIN_A_TOTAL, VITAMIN_B12_TOTAL, VITAMIN_B6_TOTAL, VITAMIN_C_TOTAL, VITAMIN_D_TOTAL, VITAMIN_E_TOTAL, VITAMIN_K_TOTAL, ZINC_TOTAL
PowerRecord POWER_AVG, POWER_MAX, POWER_MIN
RestingHeartRateRecord BPM_AVG, BPM_MAX, BPM_MIN
SkinTemperatureRecord TEMPERATURE_DELTA_AVG, TEMPERATURE_DELTA_MAX, TEMPERATURE_DELTA_MIN
SleepSessionRecord SLEEP_DURATION_TOTAL
SpeedRecord SPEED_AVG, SPEED_MAX, SPEED_MIN
StepsRecord COUNT_TOTAL
StepsCadenceRecord RATE_AVG, RATE_MAX, RATE_MIN
TotalCaloriesBurnedRecord ENERGY_TOTAL
WeightRecord WEIGHT_AVG, WEIGHT_MAX, WEIGHT_MIN
WheelchairPushesRecord COUNT_TOTAL