헬스 커넥트는 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_STEPSandroid.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 */ }