使用 Recording API 記錄健身資料

應用程式可透過行動裝置上的 Recording API,以省電的方式記錄健身資料。舉例來說,您可以使用這個 API 記錄步數,就像計步器擷取步數資料一樣。這個 API 無須帳戶,也就是說,使用這項服務時不需要 Google 帳戶,資料會儲存在裝置上。

如果應用程式需要讀取各種來源的其他健康與健身資料 (不只是裝置上的步數),建議整合 健康資料同步。在 Android 14 (API 級別 34) 以上版本中,健康資料同步還能提供裝置端步數的存取權。

本指南說明如何在健康與健身服務的行動版中使用 Recording API。

如需範例,請參閱 GitHub 上的「Recording API on mobile sample」。

重要須知

行動版 Recording API 獨有的重要功能包括:

  • 開始或續訂錄影方案後,即可存取最新訂閱方案生效後最多 10 天的資料。
  • 只有在有效訂閱期間,系統才會提供資料。如果透過呼叫 unsubscribe 移除訂閱項目,您就無法存取收集到的資料。

資料類型

行動版 Recording API 可記錄下列資料類型:

立即開始

如要開始使用,請在 build.gradle 檔案中新增下列依附元件:

Kotlin DSL

plugin {
  id("com.android.application")
}

...

dependencies {
  implementation("com.google.android.gms:play-services-fitness:21.2.0")
}

Groovy DSL

apply plugin: 'com.android.application'

...

dependencies {
  implementation 'com.google.android.gms:play-services-fitness:21.2.0'
}

要求權限

如要在行動裝置上使用 Recording API 記錄資料,應用程式必須要求下列權限

  • android.permission.ACTIVITY_RECOGNITION

檢查 Play 服務版本

如要在行動裝置上使用 Recording API,使用者必須將 Google Play 服務更新至 LOCAL_RECORDING_CLIENT_MIN_VERSION_CODE。你可以使用 isGooglePlayServicesAvailable 方法檢查這項設定:

val hasMinPlayServices = isGooglePlayServicesAvailable(context, LocalRecordingClient.LOCAL_RECORDING_CLIENT_MIN_VERSION_CODE)

if(hasMinPlayServices != ConnectionResult.SUCCESS) {
  // Prompt user to update their device's Google Play services app and return
}

// Continue with Recording API functions

否則,如果使用者的 Google Play 服務版本過舊,系統會擲回 ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED 例外狀況。

訂閱健身資料

如要要求在背景收集步數資料,請使用 subscribe 方法,如以下程式碼片段所示:

val localRecordingClient = FitnessLocal.getLocalRecordingClient(this)
// Subscribe to steps data
localRecordingClient.subscribe(LocalDataType.TYPE_STEP_COUNT_DELTA)
  .addOnSuccessListener {
    Log.i(TAG, "Successfully subscribed!")
  }
  .addOnFailureListener { e ->
    Log.w(TAG, "There was a problem subscribing.", e)
  }

讀取及處理健身資料

訂閱後,請使用 readData 方法要求資料。接著,您可以發出 LocalDataReadRequest,從產生的 LocalDataSet 取得 LocalDataPoints,如下列程式碼片段所示:

val endTime = LocalDateTime.now().atZone(ZoneId.systemDefault())
val startTime = endTime.minusWeeks(1)
val readRequest =
  LocalDataReadRequest.Builder()
    // The data request can specify multiple data types to return,
    // effectively combining multiple data queries into one call.
    // This example demonstrates aggregating only one data type.
    .aggregate(LocalDataType.TYPE_STEP_COUNT_DELTA)
    // Analogous to a "Group By" in SQL, defines how data should be
    // aggregated. bucketByTime allows bucketing by time span.
    .bucketByTime(1, TimeUnit.DAYS)
    .setTimeRange(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
    .build()

  localRecordingClient.readData(readRequest).addOnSuccessListener { response ->
    // The aggregate query puts datasets into buckets, so flatten into a
    // single list of datasets.
    for (dataSet in response.buckets.flatMap { it.dataSets }) {
      dumpDataSet(dataSet)
    }
  }
  .addOnFailureListener { e ->
    Log.w(TAG,"There was an error reading data", e)
  }

fun dumpDataSet(dataSet: LocalDataSet) {
  Log.i(TAG, "Data returned for Data type: ${dataSet.dataType.name}")
  for (dp in dataSet.dataPoints) {
    Log.i(TAG,"Data point:")
    Log.i(TAG,"\tType: ${dp.dataType.name}")
    Log.i(TAG,"\tStart: ${dp.getStartTime(TimeUnit.HOURS)}")
    Log.i(TAG,"\tEnd: ${dp.getEndTime(TimeUnit.HOURS)}")
    for (field in dp.dataType.fields) {
      Log.i(TAG,"\tLocalField: ${field.name.toString()} LocalValue: ${dp.getValue(field)}")
    }
  }
}

LocalRecordingClient 會持續更新資料收集內容。您隨時可以使用 readData 提取最新數據。

請注意,LocalRecordingClient 最多會儲存 10 天的資料。為降低資料遺失的風險,您可以使用 WorkManager 定期在背景收集資料。

取消訂閱健身資料

為釋出資源,請務必在應用程式不再需要感應器資料時,取消訂閱感應器資料的收集作業。如要取消訂閱,請使用 unsubscribe 方法:

val localRecordingClient = FitnessLocal.getLocalRecordingClient(this)
// Unsubscribe from steps data
localRecordingClient.unsubscribe(LocalDataType.TYPE_STEP_COUNT_DELTA)
  .addOnSuccessListener {
    Log.i(TAG, "Successfully unsubscribed!")
  }
  .addOnFailureListener { e ->
    Log.w(TAG, "There was a problem unsubscribing.", e)
  }