Ghi lại dữ liệu về hoạt động thể dục bằng Recording API

Recording API trên thiết bị di động cho phép ứng dụng của bạn ghi lại dữ liệu về hoạt động thể dục trên thiết bị di động theo cách tiết kiệm pin. Ví dụ: sử dụng API này để ghi lại số bước, tương tự như một máy đo bước chân truy xuất dữ liệu số bước. API này không cần tài khoản, tức là bạn không cần có Tài khoản Google để sử dụng dịch vụ và dữ liệu được lưu trữ trên thiết bị.

Hướng dẫn này cho bạn biết cách sử dụng Recording API trên thiết bị di động trong các trải nghiệm về sức khoẻ và thể dục.

Hãy xem Mẫu Recording API trên thiết bị di động trên GitHub để biết ví dụ.

Những chi tiết đáng chú ý

Có một số tính năng đáng chú ý chỉ có trong Recording API trên thiết bị di động:

  • Sau khi gói thuê bao ghi hình bắt đầu hoặc được gia hạn, bạn có thể truy cập vào dữ liệu kể từ gói thuê bao gần đây nhất (tối đa 10 ngày).
  • Dữ liệu chỉ có sẵn khi có một gói thuê bao đang hoạt động. Nếu một thuê bao bị xoá bằng cách gọi unsubscribe, thì dữ liệu đã thu thập sẽ không truy cập được.

Loại dữ liệu

Recording API trên thiết bị di động có thể ghi lại các loại dữ liệu sau:

Bắt đầu

Để bắt đầu, hãy thêm phần phụ thuộc sau vào tệp 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'
}

Yêu cầu cấp quyền

Để ghi dữ liệu bằng Recording API trên thiết bị di động, ứng dụng của bạn sẽ cần yêu cầu quyền sau:

  • android.permission.ACTIVITY_RECOGNITION

Kiểm tra phiên bản Dịch vụ Play

Để sử dụng Recording API trên thiết bị di động, người dùng phải cập nhật Dịch vụ Google Play lên phiên bản LOCAL_RECORDING_CLIENT_MIN_VERSION_CODE. Bạn có thể kiểm tra việc này bằng phương thức 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

Nếu phiên bản Dịch vụ Google Play của người dùng quá thấp, hệ thống sẽ gửi ra một ngoại lệ ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED.

Đăng ký nhận dữ liệu về hoạt động thể chất

Để yêu cầu thu thập dữ liệu về số bước ở chế độ nền, hãy sử dụng phương thức subscribe, như minh hoạ trong đoạn mã sau:

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

Đọc và xử lý dữ liệu về hoạt động thể chất

Sau khi đăng ký, hãy yêu cầu dữ liệu bằng phương thức readData. Sau đó, bạn có thể lấy LocalDataPoints từ LocalDataSet thu được bằng cách tạo một LocalDataReadRequest, như minh hoạ trong đoạn mã sau:

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 liên tục cập nhật bộ sưu tập dữ liệu của mình. Bạn có thể sử dụng readData để lấy số liệu mới nhất bất cứ lúc nào.

Xin lưu ý rằng LocalRecordingClient lưu trữ dữ liệu tối đa 10 ngày. Để giảm nguy cơ mất dữ liệu, bạn có thể dùng WorkManager để định kỳ thu thập dữ liệu ở chế độ nền.

Huỷ đăng ký nhận dữ liệu về hoạt động thể dục

Để giải phóng tài nguyên, bạn nên nhớ huỷ đăng ký thu thập dữ liệu cảm biến khi ứng dụng không cần dữ liệu đó nữa. Để huỷ đăng ký, hãy sử dụng phương thức 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)
  }