Melacak langkah

Health Connect menyediakan jenis data langkah untuk mencatat jumlah langkah menggunakan StepsRecord. Langkah adalah pengukuran dasar dalam pelacakan kesehatan dan kebugaran.

Panduan ini kompatibel dengan Health Connect versi 1.1.0-alpha12 atau yang lebih baru.

Membaca langkah-langkah seluler

Dengan Android 14 (level API 34) dan SDK Extension versi 20 atau yang lebih tinggi, Health Connect menyediakan penghitungan langkah di perangkat. Jika ada aplikasi yang telah diberi izin READ_STEPS, Health Connect akan mulai mencatat langkah dari perangkat yang didukung Android, dan pengguna akan melihat data langkah otomatis ditambahkan ke entri Langkah Health Connect.

Untuk memeriksa apakah penghitungan langkah di perangkat tersedia, pastikan perangkat menjalankan Android 14 (level API 34) dan memiliki setidaknya ekstensi SDK versi 20:

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

Jika aplikasi Anda membaca jumlah langkah gabungan menggunakan aggregate dan tidak memfilter menurut DataOrigin, langkah di perangkat akan otomatis disertakan dalam total, dan tidak diperlukan perubahan untuk update Juni 2026.

Perubahan atribusi untuk langkah-langkah di perangkat

Mulai update Juni 2026, langkah yang dilacak secara native oleh Health Connect diatribusikan ke Nama Paket Sintetis (SPN), seperti com.android.healthconnect.phone.jd5bdd37e1a8d3667a05d0abebfc4a89e.

Sebelumnya, langkah-langkah bawaan diatribusikan ke nama paket android. Data langkah historis yang dicatat sebelum Juni 2026 mempertahankan nama paket android.

SPN bersifat khusus perangkat dan dicakup per aplikasi untuk melindungi privasi pengguna:

  • Stabil: SPN untuk perangkat saat ini stabil untuk aplikasi Anda.
  • Cakupan Aplikasi: Aplikasi yang berbeda di perangkat yang sama akan melihat SPN yang berbeda untuk data langkah di perangkat.

Kueri untuk langkah-langkah di perangkat

Karena SPN tercakup dan spesifik per perangkat, Anda tidak boleh melakukan hardcode pada nilai SPN. Sebagai gantinya, gunakan getCurrentDeviceDataSource API untuk mengambil SPN untuk perangkat saat ini.

Meskipun penghitungan langkah di perangkat memerlukan SDK extension versi 20 atau yang lebih tinggi, getCurrentDeviceDataSource() API tersedia di Android 14 (level API 34) dengan SDK extension versi 22 atau yang lebih tinggi. Untuk menggunakan API ini, tetapkan compileSdkExtension ke 22 atau yang lebih tinggi di file build.gradle.kts atau build.gradle level modul:

Kotlin

android {
    compileSdk = 35
    compileSdkExtension = 22
}

Groovy

android {
    compileSdk 35
    compileSdkExtension 22
}

getCurrentDeviceDataSource() API belum tersedia di library Jetpack Health Connect. Contoh berikut menggunakan API framework Android, yang memerlukan callback Executor dan OutcomeReceiver:

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
        }
    }
)

Jika aplikasi Anda perlu membaca langkah-langkah di perangkat, atau jika aplikasi menampilkan data langkah yang dikelompokkan berdasarkan aplikasi atau perangkat sumber, Anda harus mengkueri data yang DataOrigin-nya adalah android atau cocok dengan SPN perangkat. Jika aplikasi Anda menampilkan atribusi untuk data langkah, gunakan metadata.device untuk mengidentifikasi perangkat sumber untuk setiap data. Untuk langkah-langkah di perangkat yang diidentifikasi oleh SPN dalam data gabungan, Anda dapat menggunakan metadata perangkat seperti model atau manufacturer dari DeviceDataSource untuk atribusi, atau menggunakan label umum seperti "Ponsel Anda" untuk langkah-langkah di perangkat.

Contoh berikut menunjukkan cara membaca data jumlah langkah di perangkat yang digabungkan dengan memfilter android dan SPN perangkat saat ini:

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.
    }
}

Penghitungan Langkah di Perangkat

  • Penggunaan Sensor: Health Connect menggunakan sensor TYPE_STEP_COUNTER dari SensorManager. Sensor ini dioptimalkan untuk konsumsi daya rendah, sehingga ideal untuk pelacakan langkah latar belakang berkelanjutan.
  • Perincian Data: Untuk menghemat masa pakai baterai, data langkah biasanya di-batch dan ditulis ke database Health Connect tidak lebih sering dari sekali per menit.
  • Atribusi: Langkah-langkah yang dicatat oleh fitur ini sebelum Juni 2026 diatribusikan ke nama paket android di DataOrigin. Setelah tanggal ini, SPN akan diatribusikan ke SPN khusus perangkat. Lihat Perubahan atribusi untuk langkah-langkah di perangkat.
  • Aktivasi: Mekanisme penghitungan langkah di perangkat hanya aktif jika setidaknya satu aplikasi di perangkat telah diberi izin READ_STEPS dalam Health Connect.

Memeriksa ketersediaan Health Connect

Sebelum mencoba menggunakan Health Connect, aplikasi Anda harus memverifikasi bahwa Health Connect tersedia di perangkat pengguna. Health Connect mungkin tidak diinstal di semua perangkat atau mungkin dinonaktifkan. Anda dapat memeriksa ketersediaan menggunakan metode HealthConnectClient.getSdkStatus().

Cara memeriksa ketersediaan Health Connect

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
}

Bergantung pada status yang ditampilkan oleh getSdkStatus(), Anda dapat memandu pengguna untuk menginstal atau mengupdate Health Connect dari Google Play Store jika perlu.

Izin yang diperlukan

Akses ke langkah-langkah dilindungi oleh izin berikut:

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

Untuk menambahkan kemampuan langkah ke aplikasi Anda, mulailah dengan meminta izin untuk jenis data Steps.

Berikut adalah izin yang harus Anda deklarasikan agar dapat menulis langkah:

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

Untuk membaca langkah, Anda perlu meminta izin berikut:

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

Meminta izin dari pengguna

Setelah membuat instance klien, aplikasi Anda perlu meminta izin dari pengguna. Pengguna harus diizinkan untuk memberikan atau menolak izin kapan saja. Untuk melakukannya, buat kumpulan izin untuk jenis data yang diperlukan. Pastikan izin dalam kumpulan dideklarasikan dalam manifes Android Anda terlebih dahulu.

val permissions =
    setOf(
        HealthPermission.getReadPermission(StepsRecord::class),
        HealthPermission.getWritePermission(StepsRecord::class)
    )
Gunakan getGrantedPermissions untuk mengetahui apakah aplikasi Anda sudah mendapatkan izin yang diperlukan. Jika belum, gunakan createRequestPermissionResultContract untuk meminta izin tersebut. Tindakan ini akan menampilkan layar izin Health Connect.
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.") }
    }
}
Karena pengguna dapat memberikan atau mencabut izin kapan saja, aplikasi Anda harus memeriksa izin setiap kali sebelum menggunakannya dan menangani skenario saat izin hilang.

Informasi yang disertakan dalam kumpulan data Langkah

Setiap StepsRecord berisi informasi berikut:

  • count: Jumlah langkah yang dilakukan dalam interval waktu, sebagai Long.
  • startTime: Waktu mulai interval pengukuran.
  • endTime: Waktu akhir interval pengukuran.
  • startZoneOffset: Offset zona untuk waktu mulai.
  • endZoneOffset: Selisih zona untuk waktu berakhir.

Agregasi yang didukung

Nilai gabungan berikut tersedia untuk StepsRecord:

Nilai gabungan berikut tersedia untuk StepsCadenceRecord:

Contoh penggunaan

Bagian berikut menunjukkan cara membaca dan menulis data StepsRecord.

Menulis data langkah

Aplikasi Anda dapat menulis data jumlah langkah dengan menyisipkan instance StepsRecord. Contoh berikut menunjukkan cara mencatat 1.000 langkah yang dilakukan pengguna:

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))

Membaca data gabungan

Cara paling umum untuk membaca data langkah adalah dengan menggabungkan total langkah selama jangka waktu tertentu. Contoh berikut menunjukkan cara membaca total jumlah langkah untuk pengguna dalam rentang waktu tertentu:

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
}

Membaca data mentah

Contoh berikut menunjukkan cara membaca data StepsRecord mentah antara waktu mulai dan waktu akhir:

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