在背景監控資料

如果應用程式需要在背景監控健康照護服務資料,就適合採用被動資料更新。此功能是針對跨數小時、數天或更長時間的用途設計。如果您需要在應用程式未運作、使用者並未明顯處於運動狀態時儲存或處理健康資料,則請使用健康照護服務的被動用戶端。

如需被動資料使用範例,請參閱 GitHub 上的 被動資料被動目標範例。

新增依附元件

如要為健康照護服務新增依附元件,必須將 Google Maven 存放區新增至專案。詳情請參閱「Google 的 Maven 存放區」。

請在模組層級 build.gradle 檔案中新增以下依附元件:

Groovy

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

Kotlin

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

檢查功能

在註冊資料更新內容之前,請先檢查裝置是否可以提供應用程式所需的資料類型。您可以利用檢查作業啟用/停用特定功能或修改應用程式的 UI,彌補未提供的功能。

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
}

註冊被動資料

您可以透過服務和/或回呼接收被動資料。當應用程式的任何部分未顯示於前景時,應用程式可以利用服務在背景接收資料。背景接收資料作業是採分批進行。相較之下,回呼功能接收資料的速度更快一些,但僅適用於應用程式運作且成功通知回呼的情況。

無論您使用何種方法,請先建立 PassiveListenerConfig 決定要接收的資料類型,如以下範例所示:

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

如要使用回呼功能接收資料,請定義並註冊回呼,如以下範例所示:

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

服務的使用方式與回呼類似,但此時不應建立從 PassiveListenerCallback 衍生的類別,而是要從 PassiveListenerService 衍生,如以下範例所示:

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

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

接著,請在 AndroidManifest.xml 檔案中聲明服務。您需具備健康照護服務權限,確保只有健康照護服務可以繫結至該服務:

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

解譯時間

從健康照護服務接收資料時會分批進行,因此可能會在同一批次中收到不同類型的資料點,或收到相同類型的多個資料點。使用這些物件中包含的時間戳記 (而非應用程式收到的時間),以便判斷事件的正確順序。

您可以先計算啟動時間戳記,然後取得各個 DataPoint 的時間戳記,如以下範例所示:

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

這個值隨後可以傳遞至 getStartInstant()getEndInstant()

在啟動後恢復註冊申請

重新啟動後,系統不會保留被動資料註冊。如要在裝置重新啟動後接收資料,請使用會監聽 ACTION_BOOT_COMPLETED 系統廣播訊息的 BroadcastReceiver 重新建立註冊。

在接收端中,請勿嘗試直接還原註冊,而是應將這項功能委派給 WorkManager worker。裝置啟動時,健康照護服務可能需要 10 秒以上時間才能確認被動資料註冊要求,這可能會超過 BroadcastReceiver 允許的執行時間。相比之下,WorkManager worker 的執行時間上限為 10 分鐘

下列程式碼片段為 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()
   }
}

如要安排系統在裝置啟動時執行此程式碼,請對 AndroidManifest.xml 檔案進行兩項變更。

首先,請將下列權限新增為 <manifest> 的子項:

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

接著,將下列接收器意圖篩選器新增為 <application> 的子項:

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

活動狀態

被動用戶端也可以提供使用者狀態的概略資訊,例如是否正在睡眠。如要接收這類更新,請按照下列步驟操作:

  1. 要求 ACTIVITY_RECOGNITION 權限。
  2. PassiveListenerConfig 建構工具中呼叫 setShouldUserActivityInfoBeRequested(true)

建議您在回呼或服務中覆寫 onUserActivityInfoReceived() 方法,並使用傳回的 UserActivityInfo,如以下範例所示:

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) {
        // ...
    }
}

被動目標

您可以設定被動用戶端在達到被動目標時通知應用程式,例如使用者在一天內走完 10,000 步時。

如要執行此操作,請設定目標,如以下範例所示:

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

將這個目標加入 PassiveListenerConfig,如以下範例所示:

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

請在回呼或服務中覆寫 onGoalCompleted() 方法,並使用傳回的 PassiveGoal,如以下範例所示:

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