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

API Ghi trên thiết bị di động cho phép ứng dụng của bạn ghi lại số bước trên thiết bị di động theo cách tiết kiệm pin. API này không có tài khoản, tức là không yêu cầu 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 Ghi âm trên thiết bị di động để mang đến trải nghiệm về sức khoẻ và thể hình.

Chi tiết đáng chú ý

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

  • Sau khi gói thuê bao ghi lại bắt đầu hoặ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 (trong tối đa 10 ngày).
  • Bạn chỉ có thể xem dữ liệu khi có một gói thuê bao đang hoạt động. Nếu gói thuê bao bị xoá bằng cách gọi unsubscribe, thì bạn sẽ không thể truy cập vào dữ liệu bước đã thu thập.

Kiểu dữ liệu

API Ghi trên thiết bị di động có thể ghi lại những 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")
}

DSL bắt mắt

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 lại dữ liệu bằng API 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

Ngược lại, 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ý dữ liệu về thể chất

Để yêu cầu thu thập dữ liệu về số bước, quãng đường hoặc lượng calo ở 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 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ể nhận 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 hoạt động thu thập dữ liệu của mình. Bạn có thể sử dụng readData để lấy các số mới nhất bất cứ lúc nào.

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

Hủy đăng ký dữ liệu 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 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)
  }