API ghi trên thiết bị di động

API Bản ghi trên thiết bị di động cho phép ứng dụng của bạn ghi lại số bước, tương tự như một máy đếm bước truy xuất dữ liệu số bước, từ một thiết bị di động theo cách tiết kiệm pin. API này không cần tài khoản, nghĩa là bạn không cần 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 API bản ghi trên thiết bị di động trong trải nghiệm sức khoẻ và thể hình.

Thông tin chi tiết đáng chú ý

Có một số tính năng đáng chú ý dành riêng cho API bản ghi trên thiết bị di động:

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

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 API bản ghi trên thiết bị di động, ứng dụng của bạn 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 API bản ghi trên thiết bị di động, người dùng phải cập nhật Dịch vụ Google Play lên LOCAL_RECORDING_CLIENT_MIN_VERSION_CODE. Bạn có thể kiểm tra điều này bằng cách sử dụ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 không, 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 một ngoại lệ ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED.

Đăng ký nhận dữ liệu về 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ư 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 thể dục

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 LocalDataReadRequest, như 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 tập hợp dữ liệu. 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ể sử dụng WorkManager để thu thập định kỳ dữ liệu ở chế độ nền.

Huỷ đăng ký nhận dữ liệu về thể chất

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