활성 데이터와 운동

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

Wear OS 폼 팩터는 운동 중일 때와 같이 다른 폼 팩터가 잘 맞지 않는 상황에 적합합니다. 이러한 상황에서는 앱이 센서에서 오는 데이터를 자주 업데이트해야 할 수도 있고 사용자의 운동 관리를 적극적으로 지원할 수도 있습니다. 건강 관리 서비스는 이러한 유형의 환경을 더 쉽게 개발할 수 있는 API를 제공합니다.

GitHub의 운동 샘플을 참고하세요.

종속 항목 추가

건강 관리 서비스에 종속 항목을 추가하려면 프로젝트에 Google Maven 저장소를 추가해야 합니다. 자세한 내용은 Google의 Maven 저장소를 참고하세요.

그런 다음, 모듈 수준의 build.gradle 파일에 다음 종속 항목을 추가합니다.

Groovy

dependencies {
    implementation "androidx.health:health-services-client:1.0.0-alpha03"
}

Kotlin

dependencies {
    implementation("androidx.health:health-services-client:1.0.0-alpha03")
}

앱이 건강 관리 서비스와 상호작용할 수 있도록 AndroidManifest.xml 파일의 manifest 태그 내부에 다음을 추가합니다. 자세한 내용은 패키지 공개 상태를 참고하세요.

<queries>
    <package android:name="com.google.android.wearable.healthservices" />
</queries>

MeasureClient 사용

MeasureClient API를 사용하면 앱이 콜백을 등록하여 필요한 기간 동안 데이터를 수신합니다. 이는 앱을 사용하는 동안 빠른 데이터 업데이트가 필요한 상황을 위한 것입니다. 사용자가 인지할 수 있도록 포그라운드 UI를 사용하여 만드는 것이 이상적입니다.

기능 확인

데이터 업데이트를 등록하기 전에 기기가 앱에서 필요한 데이터 유형을 제공할 수 있는지 확인해야 합니다. 사전에 기능을 확인하면 특정 기능을 사용 설정하거나 중지할 수 있고 사용할 수 없는 기능을 보완하도록 앱의 UI를 수정할 수 있습니다.

Kotlin


val healthClient = HealthServices.getClient(this /*context*/)
val measureClient = healthClient.measureClient
lifecycleScope.launch {
    val capabilities = measureClient.capabilities.await()
    supportsHeartRate =
        DataType.HEART_RATE_BPM in capabilities.supportedDataTypesMeasure
}

자바


HealthServicesClient healthClient = HealthServices.getClient(this /*context*/);
ListenableFuture<MeasureCapabilities> capabilitiesFuture =
        healthClient.getCapabilities();
Futures.addCallback(capabilitiesFuture,
        new FutureCallback<Capabilities>() {
            @Override
            public void onSuccess(@Nullable Capabilities result) {
                boolean supportsHeartRate = result
                        .supportedDataTypesMeasure()
                        .contains(DataType.HEART_RATE_BPM)
            }

            @Override
            public void onFailure(Throwable t) {
                // display an error
            }
        },
        ContextCompat.getMainExecutor(this /*context*/));

데이터 등록

등록된 각 콜백은 단일 데이터 유형을 위한 것입니다. 일부 데이터 유형은 가용 상태가 다를 수 있습니다. 예를 들어, 기기가 손목에 제대로 연결되어 있지 않으면 심박수 데이터가 제공되지 않을 수 있습니다.

콜백으로 인해 센서 샘플링 레이트가 증가하고 이에 따라 전력 소모가 증가하므로 콜백이 등록되는 시간을 최소화하는 것이 중요합니다.

val heartRateCallback = object : MeasureCallback {
    override fun onAvailabilityChanged(type: DataType, availability: Availability) {
        if (availability is DataTypeAvailability) {
            // Handle availability change.
        }
    }

    override fun onData(dataPoints: List<DataPoint>) {
        // Inspect data points.
    }
}
val healthClient = HealthServices.getClient(this /*context*/)

// Register the callback.
lifecycleScope.launch {
    healthClient.measureClient
        .registerCallback(DataType.HEART_RATE_BPM, heartRateCallback)
        .await()
}

// Unregister the callback.
lifecycleScope.launch {
    healthClient.measureClient.unregisterCallback(heartRateCallback).await()
}

ExerciseClient 사용

건강 관리 서비스는 ExerciseClient를 통해 운동 앱을 최고 수준으로 지원합니다. ExerciseClient를 사용하면 앱에서 운동의 진행 시점을 제어할 수 있고 운동 목표를 추가하며 운동 상태 및 다른 원하는 측정항목의 업데이트를 받을 수 있습니다. 자세한 내용은 건강 관리 서비스가 지원하는 ExerciseType의 전체 목록을 참고하세요.

앱 구조

건강 관리 서비스로 운동 앱을 빌드하는 경우 다음 앱 구조를 사용합니다. 화면과 탐색은 기본 활동 내에 있어야 합니다. 운동 상태, 센서 데이터, 진행 중인 활동 및 데이터는 포그라운드 서비스를 사용하여 관리합니다. Room을 사용하여 데이터를 저장하고 Work Manager를 사용하여 데이터를 업로드합니다.

운동을 준비 중이거나 운동 중에는 다양한 이유로 활동이 중지될 수 있습니다. 사용자가 다른 앱으로 전환하거나 시계 화면으로 돌아갈 수 있습니다. 시스템이 활동 위에 무언가를 표시하거나 일정 시간 동안 사용하지 않으면 화면이 꺼질 수도 있습니다. 지속적으로 실행되는 ForegroundServiceExerciseClient와 함께 사용하여 운동 전체에 올바른 작업을 보장해야 합니다.

ForegroundService를 사용하면 Ongoing Activity API를 통해 시계 화면에 표시기를 표시할 수 있으며, 이를 통해 사용자가 운동으로 빠르게 돌아갈 수 있습니다.

위치 데이터를 요청할 때는 ForegroundService를 사용해야 합니다. 매니페스트 파일은 foregroundServiceType="location 및 적절한 권한을 지정해야 합니다.

활동에 대기 모드를 지원하는 것이 좋습니다. 자세한 내용은 Wear에서 앱 표시 유지를 참고하세요.

기능 확인

ExerciseType은 측정항목과 운동 목표를 위해 특정 데이터 유형을 지원합니다. 지원되는 데이터 유형은 기기에 따라 다를 수 있으므로 시작 시 이러한 기능을 확인해야 합니다. 기기가 특정 운동 유형을 지원하지 않거나 자동 일시중지와 같은 기능이 없을 수도 있습니다. 또한, 시간이 지나면서 소프트웨어 업데이트 등으로 기기의 기능이 변경될 수 있습니다.

앱은 앱 시작 시 기기 기능을 쿼리하고 플랫폼에서 지원하는 운동, 각 운동에 지원되는 기능(예: 자동 일시중지), 각 운동에 지원되는 데이터 유형 및 이러한 데이터 유형 각각에서 요구하는 권한을 저장하고 처리해야 합니다.

원하는 운동 유형과 함께 ExerciseCapabilities.getExerciseTypeCapabilities()를 사용하여 요청할 수 있는 측정항목의 종류, 설정할 수 있는 운동 목표, 운동 유형에서 제공하는 기타 기능을 확인할 수 있습니다.

val healthClient = HealthServices.getClient(this /*context*/)
val exerciseClient = healthClient.exerciseClient
lifecycleScope.launch {
    val capabilities = exerciseClient.capabilities.await()
    if (ExerciseType.RUNNING in capabilities.supportedExerciseTypes) {
        runningCapabilities =
            capabilities.getExerciseTypeCapabilities(ExerciseType.RUNNING)
    }
}

반환된 ExerciseTypeCapabilities의 내부에 있는 supportedExerciseTypes에는 데이터를 요청할 수 있는 DataType이 나열됩니다. 이는 기기에 따라 다르므로 지원되지 않는 DataType을 요청하지 않도록 주의해야 합니다. 그러지 않으면 요청이 실패할 수 있습니다.

supportedGoalssupportedMilestones 필드는 키가 DataType이고 값이 연결된 DataType과 함께 사용할 수 있는 ComparisonType 집합인 위치에 매핑됩니다. 이를 사용하여 운동이 만들려는 운동 목표를 지원할 수 있는 운동인지 판단합니다.

앱에서 사용자가 자동 일시중지 또는 랩 기능을 사용하도록 허용하면 앱은 기기에서 이 기능을 지원하는지 확인해야 합니다. 각 기능에 맞게 supportsAutoPauseAndResume 또는 supportsLaps를 사용합니다. ExerciseClient는 기기에서 지원하지 않는 요청을 거부합니다.

// Whether we can request heart rate metrics.
supportsHeartRate = DataType.HEART_RATE_BPM in runningCapabilities.supportedDataTypes

// Whether we can make a one-time goal for aggregate steps.
val stepGoals = runningCapabilities.supportedGoals[DataType.STEPS]
supportsStepGoals =
    (stepGoals != null && ComparisonType.GREATER_THAN_OR_EQUAL in stepGoals)

// Whether auto-pause is supported
val supportsAutoPause = runningCapabilities.supportsAutoPauseAndResume

// Whether laps is supported
val supportsLaps = runningCapabilities.supportsLaps

운동 상태 업데이트 등록

운동 업데이트는 리스너로 전송됩니다. 앱은 한 번에 하나의 리스너만 등록할 수 있습니다. 운동을 시작하기 전에 리스너를 설정해야 합니다. 리스너는 앱이 소유한 운동의 업데이트만 수신합니다.

val listener = object : ExerciseUpdateListener {
    override fun onExerciseUpdate(update: ExerciseUpdate) {
        // Process the latest information about the exercise.
        exerciseStatus = update.state // e.g. ACTIVE, USER_PAUSED, etc.
        activeDuration = update.activeDuration // Duration
        latestMetrics = update.latestMetrics // Map<DataType, List<DataPoint>>
        latestAggregateMetrics = update.latestAggregateMetrics // Map<DataType, AggregateDataPoint>
        latestGoals = update.latestAchievedGoals // Set<AchievedExerciseGoal>
        latestMilestones = update.latestMilestoneMarkerSummaries // Set<MilestoneMarkerSummary>

    }

    override fun onLapSummary(lapSummary: ExerciseLapSummary) {
        // For ExerciseTypes that support laps, this is called when a lap is marked.
    }

    override fun onAvailabilityChanged(dataType: DataType, availability: Availability) {
        // Called when the availability of a particular DataType changes.
        when {
            availability is LocationAvailability -> // Relates to Location / GPS
            availability is DataTypeAvailability -> // Relates to another DataType
        }
    }
}
val exerciseClient = HealthServices.getClient(this /*context*/).exerciseClient

// Register the listener.
lifecycleScope.launch {
    exerciseClient.setUpdateListener(listener).await()
}

운동 전체 기간 관리

건강 관리 서비스는 기기의 모든 앱에서 한 번에 최대 한 가지 운동만 지원합니다. 앱에서 운동을 시작하면 현재 다른 앱에서 진행 중인 운동은 종료됩니다. 앱은 운동을 시작하기 전에 실행 중인 운동이 있는지 확인하고 그에 맞게 반응해야 합니다. 예를 들어 시작하기 전에 사용자에게 확인을 요청합니다.

사용자가 앱을 실행할 때 앱에 건강 관리 서비스로 실행되는 기존의 운동이 있고 이 운동을 계속하는 것이 좋은지 확인해야 합니다. 앱을 종료해도 건강 관리 서비스는 운동을 자동 중지하지 않으므로 앱에 속한 기존 운동이 이미 진행 중일 수 있습니다. 현재 앱에서 소유한 활성 운동이 있는 경우 앱은 운동 추적을 위한 화면으로 전환하고 리스너를 통해 계속해서 운동 업데이트를 처리해야 합니다.

lifecycleScope.launch {
    val exerciseInfo = exerciseClient.currentExerciseInfo.await()
    when (exerciseInfo.exerciseTrackedStatus) {
        OTHER_APP_IN_PROGRESS -> // Warn user before continuing, will stop the existing workout
        OWNED_EXERCISE_IN_PROGRESS -> // This app has an existing workout
        NO_EXERCISE_IN_PROGRESS -> // Start a fresh workout
    }
}

권한

ExerciseClient를 사용할 때는 앱이 필요한 권한을 요청하고 유지해야 합니다. 앱에서 LOCATION 데이터를 사용하는 경우에는 권한과 관련하여 추가로 검토할 사항이 있습니다.

모든 데이터 유형의 경우 prepareExercise() 또는 startExercise()를 호출하기 전에 다음을 실행합니다.

  • DataType 참조 문서에서 필요한 권한을 확인합니다.
  • AndroidManifest.xml에 이러한 권한을 지정하고 사용자가 필요한 권한을 부여했는지 확인합니다. 자세한 내용은 앱 권한 요청을 참고하세요. 아직 필요한 권한이 부여되지 않은 경우 건강 관리 서비스는 요청을 거부합니다.

위치 데이터의 경우 다음 단계를 추가로 실행합니다.

  • isProviderEnabled(LocationManager.GPS_PROVIDER)를 사용하여 기기에서 GPS가 사용 설정되어 있는지 확인합니다. 필요한 경우 위치 설정을 열라는 메시지를 사용자에게 표시합니다.
  • 운동 전반에 걸쳐 적절한 foregroundServiceType을 사용하는 ForegroundService가 유지되도록 합니다.

운동 준비

GPS나 심박수와 같은 일부 센서는 워밍업하는 데 약간의 시간이 걸릴 수 있고 사용자가 운동을 시작하기 전에 데이터를 확인하고자 할 수도 있습니다. 선택사항인 prepareExercise() 메서드를 사용하면 운동 타이머를 시작하지 않고도 센서를 워밍업하고 데이터를 수신할 수 있습니다. 이 작업은 activeDuration에 영향을 미치지 않습니다.

prepareExercise()를 호출하기 전에 앱은 다음 몇 가지 검사를 실행해야 합니다.

  • 앱은 플랫폼의 위치 설정을 확인해야 합니다. 위치 설정이 중지된 경우 사용자에게 위치 액세스가 허용되지 않아 앱에서 위치를 추적할 수 없다고 알리고 사용자가 이를 사용 설정할 수 있도록 메시지를 표시합니다. 이는 사용자가 기본 설정 메뉴에서 설정한 기기 전체의 설정 확인이며 앱 수준의 권한 확인과 다릅니다.
  • 앱에 생체 신호 센서, 활동 감지, 상세한 위치 액세스에 관한 런타임 권한이 있는지 확인합니다. 권한이 누락된 경우 사용자에게 충분한 정보를 제공하는 런타임 권한 요청 메시지를 표시합니다. 사용자가 특정 권한을 부여하지 않으면 준비 호출에서 해당 권한과 연결된 데이터 유형을 삭제합니다. 생체 신호 센서와 위치 정보 액세스 권한이 모두 부여되지 않은 경우에는 준비 작업을 호출하지 마세요. 앱은 걸음 수에 기반한 이동 거리, 페이스, 속도 및 이러한 권한이 필요하지 않은 기타 측정항목을 계속 가져올 수 있습니다.

준비 호출이 성공할 수 있도록 다음을 실행합니다.

  • 준비 호출이 포함된 운동 전 활동을 위해 AmbientModeSupport를 사용합니다.
  • 포그라운드 서비스에서 prepareExercise()를 호출합니다. 메서드가 서비스에 있지 않고 활동 수명 주기에 연결된 경우 센서 준비가 불필요하게 종료될 수 있습니다.
  • 사용자가 운동 전 활동을 벗어나면 endExercise()를 호출하여 센서를 끄고 전력 소비를 줄입니다.

PREPARING 상태가 되면 센서 가용 여부가 onAvailabilityChanged()를 통해 ExerciseUpdateListener로 전송됩니다. 그러면 이 정보가 사용자에게 표시될 수 있고 사용자가 운동 시작 여부를 결정할 수 있습니다.

val dataTypes = setOf(
    DataType.HEART_RATE_BPM,
    DataType.LOCATION
)
val warmUpConfig = WarmUpConfig.Builder()
    .setDataTypes(dataTypes)
    .setExerciseType(ExerciseType.RUNNING)
    .build()
exerciseClient.prepareExercise(warmUpConfig).await()

// Data and availability updates are delivered to the registered listener

운동 시작

운동을 시작하려면 ExerciseConfig를 생성하여 운동 유형, 수신하려는 측정항목의 데이터 유형 및 운동 목표 또는 주요 기록을 설정합니다. 운동 목표는 DataType과 조건으로 구성됩니다. 운동 목표는 일회성 목표로, 조건이 충족될 때 트리거됩니다(예: 사용자가 5km를 달림). 또는 여러 번 트리거되는 운동 주요 기록을 설정할 수도 있습니다(예: 사용자가 1km를 달릴 때마다 트리거됨). 다음 샘플은 각 유형의 목표를 하나씩 보여줍니다.

const val CALORIES_THRESHOLD = 250.0
const val DISTANCE_THRESHOLD = 1_000.0 // meters

suspend fun startExercise() {
    // Types for which we want to receive metrics.
    val dataTypes = setOf(
        DataType.HEART_RATE_BPM,
        DataType.LOCATION

    )
    // Types for which we want to receive aggregate metrics.
    val aggregateDataTypes = setOf(
        DataType.DISTANCE,
        // "Total" here refers not to the aggregation but to basal + activity.
        DataType.TOTAL_CALORIES
    )

    // Create a one-time goal.
    val calorieGoal = ExerciseGoal.createOneTimeGoal(
        condition = DataTypeCondition(
            dataType = DataType.TOTAL_CALORIES,
            threshold = Value.ofDouble(CALORIES_THRESHOLD),
            comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL
        )
    )
    // Create a milestone goal. To make a milestone for every kilometer, set the initial
    // threshold to 1km and the period to 1km.
    val distanceGoal = ExerciseGoal.createMilestone(
        condition = DataTypeCondition(
            dataType = DataType.DISTANCE,
            threshold = Value.ofDouble(DISTANCE_THRESHOLD),
            comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL
        ),
        period = Value.ofDouble(DISTANCE_THRESHOLD)
    )
    val config = ExerciseConfig.builder()
        .setExerciseType(ExerciseType.RUNNING)
        .setDataTypes(dataTypes)
        .setAggregateDataTypes(aggregateDataTypes)
        .setExerciseGoals(listOf(calorieGoal, distanceGoal))
        .setShouldEnableAutoPauseAndResume(false)
        // Required for GPS for LOCATION data type, optional for some other types.
        .setShouldEnableGps(true)
        .build()
    HealthServices.getClient(this /*context*/)
        .exerciseClient
        .startExercise(config)
        .await()
}

특정 운동 유형의 경우 랩을 표시할 수도 있습니다. 건강 관리 서비스는 랩 기간동안 집계된 측정항목으로 ExerciseLapSummary를 제공합니다.

이전 예는 setShouldEnableGps의 사용 방법을 보여주며, 이 메서드는 위치 데이터를 요청할 때 true여야 합니다. 한편, GPS를 사용하면 다른 측정항목에 도움이 될 수 있습니다. ExerciseConfig의 DataType을 거리로 지정했다면 기본적으로 걸음 수를 사용하여 거리를 추정합니다. 선택사항으로 GPS를 사용 설정하면 위치 정보를 사용하여 거리를 추정할 수 있습니다.

운동 일시중지, 다시 시작, 종료

pauseExercise()endExercise()와 같은 적절한 메서드를 사용하여 운동을 일시중지, 종료 또는 다시 시작할 수 있습니다.

ExerciseUpdate의 상태를 정보 소스로 사용합니다. 운동은 pauseExercise() 호출이 반환될 때가 아니라 일시중지 상태가 ExerciseUpdate 메시지에 반영될 때 일시중지된 것으로 간주됩니다. 이는 특별히 UI 상태와 관련하여 고려해야 할 중요한 부분입니다. 사용자가 일시중지를 누르면 앱은 일시중지 버튼을 사용 중지하고 건강 관리 서비스의 pauseExercise()를 호출해야 합니다. 앱은 ExerciseUpdate.ExerciseState를 통해 건강 관리 서비스가 Paused 상태에 도달할 때까지 대기한 다음 일시중지 버튼을 계속으로 전환합니다. 이는 버튼을 누르는 것보다 건강 관리 서비스 상태 업데이트를 전송하는 데 시간이 조금 더 걸릴 수 있기 때문입니다. 따라서 버튼을 누르는 작업에 모든 UI 변경사항이 연결되면 UI가 건강 관리 서비스 상태와 동기화되지 않을 수 있습니다.

다음 상황에 유의하세요.

  • 자동 일시중지가 사용 설정됨: 사용자 상호작용 없이 운동이 일시중지되거나 시작될 수 있습니다.
  • 다른 앱에서 운동을 시작함: 사용자 상호작용 없이 운동이 종료될 수 있습니다.

앱의 운동이 다른 앱에 의해 종료된다면 앱은 종료될 수 있는 상황을 적절하게 처리해야 합니다. 앱이 종료된 경우 다음을 실행해야 합니다.

  • 사용자의 진행 상황이 삭제되지 않도록 일부 운동 상태를 저장합니다.
  • 진행 중인 활동 아이콘을 삭제하고 사용자에게 다른 앱으로 인해 운동이 종료되었음을 알리는 알림을 보냅니다.

또한 앱은 운동이 진행되는 중에 권한이 취소되는 경우도 처리해야 합니다. 이 경우 AUTO_ENDED_PERMISSIONS_LOST 상태가 isEnded를 통해 전송됩니다. 이는 종료 사례와 비슷한 방식으로 처리해야 합니다. 앱은 일부 상태를 저장하고 진행 중인 활동 아이콘을 삭제한 다음 사용자에게 발생한 문제에 관한 알림을 전송해야 합니다.

올바르게 종료되었는지 확인하는 방법은 다음 예를 참고하세요.

val listener = object : ExerciseUpdateListener {
    override fun onExerciseUpdate(update: ExerciseUpdate) {
        if (update.state.isEnded) {
            // Workout has either been ended by the user, or otherwise terminated
        }
        ...
    }

    ...
}

활성 기간 관리

운동 중 앱이 운동의 현재 활성 기간을 표시해야 할 수도 있습니다. 앱, 건강 관리 서비스, 기기 MCU(운동 추적을 담당하는 저전력 프로세서)는 모두 동일한 현재 활성 기간과 동기화되어야 합니다. 이를 관리하는 데 도움이 되도록 건강 관리 서비스는 앱이 타이머를 시작할 수 있는 앵커 포인트를 제공하는 ActiveDurationCheckpoint를 전송합니다.

활성 기간은 MCU에서 전송되고 앱에 도착하는 데 약간의 시간이 걸릴 수 있으므로 ActiveDurationCheckpoint에는 다음 두 가지 속성이 포함됩니다.

  • activeDuration - 운동이 활성화된 후 경과된 시간
  • time - 위의 활성 기간이 계산된 시점

따라서 앱에서 활성 운동의 활성 기간은 ActiveDurationCheckpoint로부터 다음 공식을 사용하여 계산할 수 있습니다.

(now() - checkpoint.time) + checkpoint.activeDuration

이 공식은 MCU에서 계산되는 활성 기간과 활성 기간이 앱에 도착하는 시점 사이의 작은 델타를 고려합니다. 이 값은 앱에서 크로노미터를 시드하는 데 사용될 수 있으며 앱의 타이머가 건강 관리 서비스 시간과 MCU 시간에 완벽하게 일치되도록 합니다.

운동이 일시중지된 경우 앱은 계산된 시간이 UI가 현재 표시하고 있는 시간을 지날 때까지 기다린 후에 UI의 타이머를 다시 시작해야 합니다. 이는 일시중지 신호가 약간 지연된 상태로 건강 관리 서비스와 MCU에 도달할 수 있기 때문입니다. 예를 들어, t=10초일 때 앱이 일시중지되면 건강 관리 서비스는 t=10.2초일 때까지 앱에 PAUSED 업데이트를 제공하지 않을 수도 있습니다.

ExerciseClient의 데이터 사용

앱이 등록한 데이터 유형의 측정항목은 ExerciseUpdate 메시지로 전송됩니다.

다음 글머리기호는 ExerciseClient의 데이터 전송 방식을 설명합니다.

  • 집계 데이터와 비집계 데이터는 latestAggregateMetricslatestMetrics 두 가지 다른 속성으로 구분됩니다.
  • 모든 ExerciseUpdate의 집계 측정항목에는 각 DataType의 최신 값만 포함됩니다.
  • 반대로, 집계하지 않는 측정항목은 각 DataType의 데이터 포인트 목록입니다. 이 목록은 데이터의 마지막 전송 이후 가져온 모든 샘플을 나타냅니다.
  • ExerciseClient는 프로세서의 절전 모드가 해제되어 있거나 최대 보고 기간에 도달했을 때(예: 150초마다)만 데이터를 전송하여 일괄 처리할 수도 있습니다.

사용자가 운동을 시작하면 ExerciseUpdate 메시지가 자주 전송될 수 있습니다(예: 1초마다). 사용자가 운동을 시작하면 화면이 꺼질 수 있습니다. 그러면 건강 관리 서비스는 기본 프로세서의 절전 모드가 해제되는 것을 방지하기 위해 더 적지만 여전히 같은 빈도로 샘플링된 데이터를 전송할 수도 있습니다. 사용자가 화면을 보면 일괄 처리 중인 모든 데이터가 즉시 앱으로 전송됩니다.

집계 측정항목

집계 측정항목은 CumulativeDataPointStatisticalDataPoint, 두 가지 다른 형식으로 제공됩니다.

예를 들어, DataType.DISTANCE는 집계될 때 CumulativeDataPoint로 표시되며 거리를 제공합니다. DataType.HEART_RATE_BPM최소, 최대, 평균으로 제공되며 StatisticalDataPoint로 표시됩니다.

DataTypes 클래스는 isCumulativeDataTypeisStatisticalDataType과 같은 도우미 메서드를 제공하여 각 AggregateDataPoint의 전송 방법을 결정하는 데 도움을 줍니다.

타임스탬프

각 데이터 포인트의 시점은 기기가 부팅된 이후의 기간을 나타냅니다. 이를 타임스탬프로 변환하려면 다음을 실행합니다.

val bootInstant =
            Instant.ofEpochMilli(System.currentTimeMillis() - SystemClock.elapsedRealtime())

그런 다음 이 값은 각 데이터 포인트에 맞게 getStartInstant() 또는 getEndInstant()와 함께 사용할 수 있습니다.

데이터 정확성

일부 데이터 유형에는 각 데이터 포인트와 연결된 정확성 정보가 포함될 수 있습니다. 이 정보는 accuracy 속성에 표시됩니다.

HEART_RATE_BPMLOCATION 데이터 유형에 맞게 각각 HrAccuracyLocationAccuracy 클래스가 채워져야 합니다. accuracy 속성이 있다면 이 속성을 사용하여 각 데이터 포인트가 애플리케이션에 충분한 정확성을 제공하는지 판단합니다.

데이터 저장 및 업로드

Room을 사용하여 건강 관리 서비스에서 제공하는 데이터를 유지할 수 있습니다. 데이터 업로드는 Work Manager와 같은 메커니즘을 사용하여 운동이 종료될 때 진행되어야 합니다. 이에 따라 운동이 종료될 때까지 데이터 업로드를 위한 네트워크 호출이 지연되고 운동 중 전력 소모가 최소화되며 서비스의 작업이 간소화됩니다.