걸음 수 추적

헬스 커넥트는 StepsRecord를 사용하여 걸음 수를 기록하는 걸음 수 데이터 유형을 제공합니다. 걸음 수는 건강 및 피트니스 모니터링의 기본 측정 항목입니다.

이 가이드는 헬스 커넥트 버전 1.1.0-alpha12 이상과 호환됩니다.

모바일 걸음 수 읽기

Android 14 (API 수준 34) 및 SDK 확장 프로그램 버전 20 이상에서 헬스 커넥트는 기기 내 걸음 수 계산을 제공합니다. 앱에 READ_STEPS 권한이 부여되면 헬스 커넥트는 Android 지원 기기에서 걸음 수를 캡처하기 시작하고 사용자는 헬스 커넥트 걸음 수 항목에 걸음 수 데이터가 자동으로 추가되는 것을 볼 수 있습니다.

기기 내 걸음 수 측정을 사용할 수 있는지 확인하려면 기기에서 Android 14 (API 수준 34)를 실행하고 SDK 확장 프로그램 버전이 20 이상인지 확인합니다.

val isStepTrackingAvailable =
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
        SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 20

앱이 aggregate를 사용하여 집계된 걸음 수를 읽고 DataOrigin로 필터링하지 않는 경우 기기 내 걸음 수가 합계에 자동으로 포함되며 2026년 6월 업데이트를 위해 변경할 필요가 없습니다.

기기 내 단계의 기여 분석 변경

2026년 6월 업데이트부터 헬스 커넥트에서 기본적으로 추적하는 걸음 수는 합성 패키지 이름(SPN)(예: com.android.healthconnect.phone.jd5bdd37e1a8d3667a05d0abebfc4a89e)에 기여한 것으로 간주됩니다.

이전에는 내장 걸음 수가 패키지 이름 android에 기여했습니다. 2026년 6월 이전에 기록된 이전 걸음 수 데이터는 android 패키지 이름을 유지합니다.

SPN은 기기별로 다르며 사용자 개인 정보를 보호하기 위해 애플리케이션별로 범위가 지정됩니다.

  • 안정적: 현재 기기의 SPN이 애플리케이션에 안정적입니다.
  • 애플리케이션 범위: 동일한 기기의 여러 애플리케이션은 기기 내 걸음 수 데이터에 대해 서로 다른 SPN을 확인합니다.

기기 내 단계 쿼리

SPN은 범위가 지정되고 기기별로 다르므로 SPN 값을 하드코딩하면 안 됩니다. 대신 getCurrentDeviceDataSource API를 사용하여 현재 기기의 SPN을 가져옵니다.

기기 내 걸음 수 계산에는 SDK 확장 프로그램 버전 20 이상이 필요하지만 getCurrentDeviceDataSource() API는 Android 14 (API 수준 34)에서 SDK 확장 프로그램 버전 22 이상으로 사용할 수 있습니다. 이 API를 사용하려면 모듈 수준 build.gradle.kts 또는 build.gradle 파일에서 compileSdkExtension를 22 이상으로 설정합니다.

Kotlin

android {
    compileSdk = 35
    compileSdkExtension = 22
}

Groovy

android {
    compileSdk 35
    compileSdkExtension 22
}

getCurrentDeviceDataSource() API는 아직 헬스 커넥트 Jetpack 라이브러리에서 사용할 수 없습니다. 다음 예에서는 Executor 및 OutcomeReceiver 콜백이 필요한 Android 프레임워크 API를 사용합니다.

import android.content.Context
import android.health.connect.DeviceDataSource
import android.health.connect.HealthConnectException
import android.health.connect.HealthConnectManager
import android.os.OutcomeReceiver

val healthConnectManager = context.getSystemService(HealthConnectManager::class.java)
healthConnectManager?.getCurrentDeviceDataSource(
    context.mainExecutor,
    object : OutcomeReceiver<DeviceDataSource, HealthConnectException> {
        override fun onResult(result: DeviceDataSource) {
            val currentDeviceSpn = result.deviceDataOrigin.packageName
        }

        override fun onError(error: HealthConnectException) {
            // Handle error
        }
    }
)

앱에서 기기 내 걸음 수를 읽어야 하거나 소스 애플리케이션 또는 기기별로 분류된 걸음 수 데이터를 표시하는 경우 DataOrigin가 android이거나 기기의 SPN과 일치하는 레코드를 쿼리해야 합니다. 앱에서 걸음 수 데이터의 기여 분석을 표시하는 경우 metadata.device를 사용하여 개별 기록의 소스 기기를 식별합니다. 집계된 데이터에서 SPN으로 식별되는 기기 내 단계의 경우 DeviceDataSource의 model 또는 manufacturer와 같은 기기 메타데이터를 기여 분석에 사용하거나 기기 내 단계에 '내 휴대전화'와 같은 일반 라벨을 사용할 수 있습니다.

다음 예는 android과 현재 기기 SPN을 모두 필터링하여 집계된 온디바이스 걸음 수 데이터를 읽는 방법을 보여줍니다.

import android.content.Context
import android.health.connect.DeviceDataSource
import android.health.connect.HealthConnectException
import android.health.connect.HealthConnectManager
import android.os.Build
import android.os.OutcomeReceiver
import android.os.ext.SdkExtensions
import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.records.StepsRecord
import androidx.health.connect.client.records.metadata.DataOrigin
import androidx.health.connect.client.request.AggregateRequest
import androidx.health.connect.client.time.TimeRangeFilter
import java.time.Instant
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlinx.coroutines.suspendCancellableCoroutine

suspend fun readDeviceStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    context: Context,
    startTime: Instant,
    endTime: Instant
) {
    // 1. Check if SDK Extension 22+ is available for getCurrentDeviceDataSource()
    val isDataSourceApiAvailable =
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
            SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 22

    try {
        val healthConnectManager = context.getSystemService(HealthConnectManager::class.java)

        // 2. Safely fetch the package name only if the API is available
        val currentDeviceSpn = if (isDataSourceApiAvailable && healthConnectManager != null) {
            suspendCancellableCoroutine { continuation ->
                healthConnectManager.getCurrentDeviceDataSource(
                    context.mainExecutor,
                    object : OutcomeReceiver<DeviceDataSource, HealthConnectException> {
                        override fun onResult(result: DeviceDataSource) {
                            continuation.resume(result.deviceDataOrigin.packageName)
                        }

                        override fun onError(error: HealthConnectException) {
                            continuation.resumeWithException(error)
                        }
                    }
                )
            }
        } else {
            null
        }

        val dataOriginFilters = mutableSetOf(DataOrigin("android"))

        // 3. Explicit null-safety check using .let
        currentDeviceSpn?.let {
            dataOriginFilters.add(DataOrigin(it))
        }

        val response = healthConnectClient.aggregate(
            AggregateRequest(
                metrics = setOf(StepsRecord.COUNT_TOTAL),
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
                dataOriginFilter = dataOriginFilters
            )
        )

        val stepCount = response[StepsRecord.COUNT_TOTAL]

    } catch (e: Exception) {
        // Now this catch block only handles actual runtime exceptions,
        // rather than Errors from missing methods.
    }
}

온디바이스 걸음 수 측정

  • 센서 사용: 헬스 커넥트는 SensorManager의 TYPE_STEP_COUNTER 센서를 사용합니다. 이 센서는 저전력 소비에 최적화되어 있어 지속적인 백그라운드 걸음 수 추적에 적합합니다.
  • 데이터 세분성: 배터리 수명을 보존하기 위해 걸음 수 데이터는 일반적으로 일괄 처리되며 1분에 한 번 이하로 헬스 커넥트 데이터베이스에 기록됩니다.
  • Attribution: 2026년 6월 이전에 이 기능으로 기록된 걸음 수는 DataOrigin의 android 패키지 이름에 기여한 것으로 간주됩니다. 이 날짜 이후에는 기기별 SPN에 기여도가 부여됩니다. 기기 내 걸음 수의 기여 분석 변경을 참고하세요.
  • 활성화: 기기 내 걸음 수 계산 메커니즘은 기기의 애플리케이션 하나 이상에 헬스 커넥트 내 READ_STEPS 권한이 부여된 경우에만 활성화됩니다.

헬스 커넥트 사용 가능 여부 확인

헬스 커넥트를 사용하기 전에 앱은 사용자의 기기에서 헬스 커넥트를 사용할 수 있는지 확인해야 합니다. 헬스 커넥트가 일부 기기에 사전 설치되어 있지 않거나 사용 중지되어 있을 수 있습니다. HealthConnectClient.getSdkStatus() 메서드를 사용하여 사용 가능 여부를 확인할 수 있습니다.

헬스 커넥트 사용 가능 여부를 확인하는 방법

fun checkHealthConnectAvailability(context: Context) {
    val providerPackageName = "com.google.android.apps.healthdata"
    val availabilityStatus = HealthConnectClient.getSdkStatus(context, providerPackageName)

    if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE) {
      // Health Connect is not available. Guide the user to install/enable it.
      // For example, show a dialog.
      return // early return as there is no viable integration
    }
    if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED) {
      // Health Connect is available but requires an update.
      // Optionally redirect to package installer to find a provider, for example:
      val uriString = "market://details?id=$providerPackageName&url=healthconnect%3A%2F%2Fonboarding"
      context.startActivity(
        Intent(Intent.ACTION_VIEW).apply {
          setPackage("com.android.vending")
          data = Uri.parse(uriString)
          putExtra("overlay", true)
          putExtra("callerId", context.packageName)
        }
      )
      return
    }
    // Health Connect is available, obtain a HealthConnectClient instance
    val healthConnectClient = HealthConnectClient.getOrCreate(context)
    // Issue operations with healthConnectClient
}

getSdkStatus()에서 반환된 상태에 따라 필요한 경우 Google Play 스토어에서 헬스 커넥트를 설치하거나 업데이트하도록 사용자를 안내할 수 있습니다.

필수 권한

걸음 수에 대한 액세스는 다음 권한으로 보호됩니다.

  • android.permission.health.READ_STEPS
  • android.permission.health.WRITE_STEPS

앱에 걸음 수 기능을 추가하려면 먼저 Steps 데이터 유형에 대한 권한을 요청하세요.

걸음 수를 쓰기 위해 선언해야 하는 권한은 다음과 같습니다.

<application>
  <uses-permission
android:name="android.permission.health.WRITE_STEPS" />
...
</application>

걸음 수를 읽으려면 다음 권한을 요청해야 합니다.

<application>
  <uses-permission
android:name="android.permission.health.READ_STEPS" />
...
</application>

사용자에게 권한 요청

클라이언트 인스턴스를 만든 후 앱은 사용자에게 권한을 요청해야 합니다. 사용자는 언제든지 권한을 부여하거나 거부할 수 있어야 합니다. 이렇게 하려면 필요한 데이터 유형의 권한 집합을 만듭니다. 집합에 있는 권한이 Android 매니페스트에 먼저 선언되어 있는지 확인합니다.

val permissions =
    setOf(
        HealthPermission.getReadPermission(StepsRecord::class),
        HealthPermission.getWritePermission(StepsRecord::class)
    )
getGrantedPermissions를 사용하여 앱에 이미 필요한 권한이 부여되었는지 확인합니다. 그렇지 않은 경우 createRequestPermissionResultContract를 사용하여 권한을 요청합니다. 그러면 헬스 커넥트 권한 화면이 표시됩니다.
val permissions = setOf(
        HealthPermission.getReadPermission(StepsRecord::class),
        HealthPermission.getWritePermission(StepsRecord::class),
        HealthPermission.getReadPermission(HeartRateRecord::class),
        HealthPermission.getWritePermission(HeartRateRecord::class)
    )

val requestPermissionsLauncher = rememberLauncherForActivityResult(
    contract = PermissionController.createRequestPermissionResultContract()
) { grantedPermissions ->
    if (grantedPermissions.containsAll(permissions)) {
        coroutineScope.launch { snackbarHostState.showSnackbar("Permissions granted!") }
    } else {
        coroutineScope.launch { snackbarHostState.showSnackbar("Permissions denied.") }
    }
}
사용자는 언제든지 권한을 부여하거나 취소할 수 있으므로 앱은 권한을 사용하기 전에 매번 권한을 확인하고 권한이 손실되는 시나리오를 처리해야 합니다.

걸음 수 기록에 포함된 정보

각 StepsRecord에는 다음 정보가 포함됩니다.

  • : 시간 간격 동안 걸은 걸음 수입니다(Long).count
  • startTime: 측정 간격의 시작 시간입니다.
  • endTime: 측정 간격의 종료 시간입니다.
  • startZoneOffset: 시작 시간의 시간대 오프셋입니다.
  • endZoneOffset: 종료 시간의 시간대 오프셋입니다.

지원되는 집계

StepsRecord에 사용할 수 있는 집계 값은 다음과 같습니다.

StepsCadenceRecord에 사용할 수 있는 집계 값은 다음과 같습니다.

사용 예시

다음 섹션에서는 StepsRecord 데이터를 읽고 쓰는 방법을 보여줍니다.

걸음 수 데이터 쓰기

앱은 StepsRecord 인스턴스를 삽입하여 걸음 수 데이터를 쓸 수 있습니다. 다음 예는 사용자가 걸은 1,000보를 기록하는 방법을 보여줍니다.

val zoneOffset = ZoneOffset.systemDefault().rules.getOffset(startTime)
val stepsRecord = StepsRecord(
    count = 120,
    startTime = startTime,
    endTime = endTime,
    startZoneOffset = zoneOffset,
    endZoneOffset = zoneOffset,
    metadata = Metadata.autoRecorded(
        device = Device(type = Device.TYPE_WATCH)
    )
)
healthConnectClient.insertRecords(listOf(stepsRecord))

집계 데이터 읽기

걸음 수 데이터를 읽는 가장 일반적인 방법은 일정 기간의 총 걸음 수를 집계하는 것입니다. 다음 예는 특정 기간 동안 사용자의 총 걸음 수를 읽는 방법을 보여줍니다.

suspend fun readStepsAggregate(startTime: Instant, endTime: Instant): Long {
    val response = healthConnectClient.aggregate(
        AggregateRequest(
            metrics = setOf(StepsRecord.COUNT_TOTAL),
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
        )
    )
    return response[StepsRecord.COUNT_TOTAL] ?: 0L
}

원시 데이터 읽기

다음 예는 시작 시간과 종료 시간 사이에 원시 StepsRecord 데이터를 읽는 방법을 보여줍니다.

val response = healthConnectClient.readRecords(
    ReadRecordsRequest(
        StepsRecord::class,
        timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
    )
)
response.records.forEach { record ->
    /* Process records */
}