Memantau data di latar belakang

Pembaruan data pasif cocok untuk aplikasi yang perlu memantau data Fitur Kesehatan di latar belakang. Pembaruan tersebut ditujukan untuk kasus penggunaan yang mencakup jam, hari, atau bahkan lebih lama. Jika Anda perlu menyimpan atau memproses data kesehatan saat aplikasi tidak berjalan, dan pengguna tidak terlibat secara eksplisit dalam latihan, gunakan klien pasif Fitur Kesehatan.

Untuk contoh penggunaan data pasif, lihat contoh Data Pasif dan Sasaran Pasif di GitHub.

Menambahkan dependensi

Untuk menambahkan dependensi di Fitur Kesehatan, Anda harus menambahkan repositori Maven Google ke project Anda. Untuk mengetahui informasi selengkapnya, lihat Repositori Maven Google.

Di file build.gradle level modul, tambahkan dependensi berikut:

Groovy

dependencies {
    implementation "androidx.health:health-services-client:1.1.0-alpha02"
}

Kotlin

dependencies {
    implementation("androidx.health:health-services-client:1.1.0-alpha02")
}

Memeriksa kemampuan

Sebelum mendaftar untuk pembaruan data, pastikan perangkat dapat menyediakan jenis data yang dibutuhkan aplikasi Anda. Dengan memeriksa kemampuan, Anda dapat mengaktifkan atau menonaktifkan fitur tertentu atau mengubah UI aplikasi untuk mengompensasi fitur yang tidak tersedia.

val healthClient = HealthServices.getClient(this /*context*/)
val passiveMonitoringClient = healthClient.passiveMonitoringClient
lifecycleScope.launchWhenCreated {
    val capabilities = passiveMonitoringClient.capabilities.await()
    // Supported types for passive data collection
    supportsHeartRate =
        DataType.HEART_RATE_BPM in capabilities.supportedDataTypesPassiveMonitoring
    // Supported types for PassiveGoals
    supportsStepsGoal =
        DataType.STEPS_DAILY in capabilities.supportedDataTypesPassiveGoals
}

Mendaftar untuk data pasif

Anda dapat menerima data pasif melalui layanan atau callback, atau keduanya. Layanan memungkinkan aplikasi Anda menerima data di latar belakang saat tidak ada bagian aplikasi yang terlihat di latar depan. Saat Anda menerima data di latar belakang, data akan dikirim dalam batch. Callback menerima data sedikit lebih cepat, tetapi hanya saat aplikasi berjalan dan callback berhasil diberi tahu.

Metode apa pun yang Anda gunakan, pertama-tama buat PassiveListenerConfig yang menentukan jenis data yang akan diterima, seperti ditunjukkan dalam contoh berikut:

val passiveListenerConfig = PassiveListenerConfig.builder()
    .setDataTypes(setOf(DataType.HEART_RATE_BPM))
    .build()

Untuk menerima data menggunakan callback, tentukan dan daftarkan callback, seperti ditunjukkan pada contoh berikut:

val passiveListenerCallback: PassiveListenerCallback = object : PassiveListenerCallback {
    override fun onNewDataPointsReceived(dataPoints: DataPointContainer) {
        // TODO: Do something with dataPoints
    }
}

passiveMonitoringClient.setPassiveListenerCallback(
    passiveListenerConfig,
    passiveListenerCallback
)

// To remove the listener
passiveMonitoringClient.clearPassiveListenerCallbackAsync()

Dengan menggunakan layanan serupa, tetapi tidak membuat class yang berasal dari PassiveListenerCallback, ambil dari PassiveListenerService, seperti yang ditunjukkan dalam contoh berikut:

class PassiveDataService : PassiveListenerService() {
    override fun onNewDataPointsReceived(dataPoints: DataPointContainer) {
        // TODO: Do something with dataPoints
    }
}

passiveMonitoringClient.setPassiveListenerServiceAsync(
    PassiveDataService::class.java,
    passiveListenerConfig
)

Selanjutnya, deklarasikan layanan di file AndroidManifest.xml Anda. Perlu izin Fitur Kesehatan, yang memastikan bahwa hanya Fitur Kesehatan yang dapat mengikat ke layanan:

<service android:name=".PassiveDataService"
    android:permission="com.google.android.wearable.healthservices.permission.PASSIVE_DATA_BINDING"
    android:exported="true" />

Menafsirkan waktu

Data yang Anda terima dari Fitur Kesehatan dikelompokkan, sehingga Anda mungkin menerima titik data dari jenis yang berbeda, atau beberapa titik data dari jenis yang sama, dalam batch yang sama. Gunakan stempel waktu yang disertakan dalam objek ini, bukan waktu saat objek tersebut diterima oleh aplikasi Anda untuk menentukan urutan peristiwa yang benar.

Dapatkan stempel waktu untuk setiap DataPoint dengan menghitung stempel waktu booting terlebih dahulu, seperti yang ditunjukkan pada contoh berikut:

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

Nilai ini kemudian dapat diteruskan ke getStartInstant() atau getEndInstant().

Memulihkan pendaftaran setelah booting

Pendaftaran data pasif tidak akan dipertahankan setiap kali mulai ulang. Untuk menerima data setelah perangkat dimulai ulang, buat ulang pendaftaran menggunakan BroadcastReceiver yang memproses siaran sistem ACTION_BOOT_COMPLETED.

Di penerima, jangan mencoba memulihkan pendaftaran secara langsung. Sebagai gantinya, delegasikan fungsi ini ke pekerja WorkManager. Saat perangkat dimulai, Fitur Kesehatan mungkin memerlukan waktu 10 detik atau lebih untuk mengonfirmasi permintaan pendaftaran data pasif, dan hal ini dapat melebihi waktu eksekusi BroadcastReceiver yang diizinkan. Sebaliknya, pekerja WorkManager memiliki batas eksekusi sepuluh menit.

Cuplikan berikut menunjukkan tampilan BroadcastReceiver:

class StartupReceiver : BroadcastReceiver() {

   override fun onReceive(context: Context, intent: Intent) {
       if (intent.action != Intent.ACTION_BOOT_COMPLETED) return


       // TODO: Check permissions first
       WorkManager.getInstance(context).enqueue(
           OneTimeWorkRequestBuilder<RegisterForPassiveDataWorker>().build()
       )
   }
}

class RegisterForPassiveDataWorker(
   private val appContext: Context,
   workerParams: WorkerParameters
) : Worker(appContext, workerParams) {

   override fun doWork(): Result {
       runBlocking {
           HealthServices.getClient(appContext)
                .passiveMonitoringClient
                .setPassiveListenerCallback(...)
       }
       return Result.success()
   }
}

Untuk mengatur sistem guna mengeksekusi kode ini saat perangkat melakukan booting, buat dua perubahan pada file AndroidManifest.xml.

Pertama, tambahkan izin berikut sebagai turunan dari <manifest>:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

Kedua, tambahkan filter intent penerima berikut sebagai turunan dari <application>:

<receiver
    android:name=".StartupReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

Status aktivitas

Klien pasif juga dapat memberikan informasi tingkat tinggi tentang status pengguna, seperti apakah pengguna sedang tidur. Untuk menerima update ini, ikuti langkah-langkah berikut:

  1. Minta izin ACTIVITY_RECOGNITION.
  2. Panggil setShouldUserActivityInfoBeRequested(true) di builder PassiveListenerConfig.

Ganti metode onUserActivityInfoReceived() dalam callback atau layanan Anda, dan gunakan UserActivityInfo yang ditampilkan, seperti ditunjukkan dalam contoh berikut:

override fun onUserActivityInfoReceived(info: UserActivityInfo) {
    val stateChangeTime: Instant = info.stateChangeTime // may be in the past!
    val userActivityState: UserActivityState = info.userActivityState
    if (userActivityState == UserActivityState.USER_ACTIVITY_ASLEEP) {
        // ...
    }
}

Sasaran pasif

Anda dapat mengonfigurasi klien pasif untuk memberi tahu aplikasi saat sasaran pasif tercapai, seperti pengguna yang menyelesaikan 10.000 langkah dalam sehari.

Untuk melakukannya, buat sasaran, seperti yang ditunjukkan pada contoh berikut:

val dailyStepsGoal by lazy {
    val condition = DataTypeCondition(
        dataType = DataType.STEPS_DAILY,
        threshold = 10_000, // Trigger every 10000 steps
        comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL
    )
    PassiveGoal(condition)
}

Tambahkan sasaran ini ke PassiveListenerConfig Anda, seperti yang ditunjukkan dalam contoh berikut:

val passiveListenerConfig = PassiveListenerConfig.builder()
    .setDailyGoals(setOf(dailyStepsGoal))
    .build()

Ganti metode onGoalCompleted() dalam callback atau layanan Anda, dan gunakan PassiveGoal yang ditampilkan, seperti ditunjukkan dalam contoh berikut:

override fun onGoalCompleted(goal: PassiveGoal) {
    when (goal.dataTypeCondition.dataType) {
        DataType.STEPS_DAILY -> {
            // ...
        }
    }
}