如需在移动设备上使用 Recording API,用户必须将 Google Play 服务更新到 LOCAL_RECORDING_CLIENT_MIN_VERSION_CODE。您可以使用 isGooglePlayServicesAvailable 方法检查此问题:
valhasMinPlayServices=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
vallocalRecordingClient=FitnessLocal.getLocalRecordingClient(this)// Subscribe to steps datalocalRecordingClient.subscribe(LocalDataType.TYPE_STEP_COUNT_DELTA).addOnSuccessListener{Log.i(TAG,"Successfully subscribed!")}.addOnFailureListener{e->
Log.w(TAG,"There was a problem subscribing.",e)}
valendTime=LocalDateTime.now().atZone(ZoneId.systemDefault())valstartTime=endTime.minusWeeks(1)valreadRequest=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(dataSetinresponse.buckets.flatMap{it.dataSets}){dumpDataSet(dataSet)}}.addOnFailureListener{e->
Log.w(TAG,"There was an error reading data",e)}fundumpDataSet(dataSet:LocalDataSet){Log.i(TAG,"Data returned for Data type: ${dataSet.dataType.name}")for(dpindataSet.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(fieldindp.dataType.fields){Log.i(TAG,"\tLocalField: ${field.name.toString()} LocalValue: ${dp.getValue(field)}")}}}
vallocalRecordingClient=FitnessLocal.getLocalRecordingClient(this)// Unsubscribe from steps datalocalRecordingClient.unsubscribe(LocalDataType.TYPE_STEP_COUNT_DELTA).addOnSuccessListener{Log.i(TAG,"Successfully unsubscribed!")}.addOnFailureListener{e->
Log.w(TAG,"There was a problem unsubscribing.",e)}
[[["易于理解","easyToUnderstand","thumb-up"],["解决了我的问题","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["没有我需要的信息","missingTheInformationINeed","thumb-down"],["太复杂/步骤太多","tooComplicatedTooManySteps","thumb-down"],["内容需要更新","outOfDate","thumb-down"],["翻译问题","translationIssue","thumb-down"],["示例/代码问题","samplesCodeIssue","thumb-down"],["其他","otherDown","thumb-down"]],["最后更新时间 (UTC):2025-08-05。"],[],[],null,["# Record fitness data using the Recording API\n\nThe Recording API on mobile allows your app to record fitness data from a mobile\ndevice in a battery-efficient way. For example, use this API to record steps,\nsimilar to a pedometer retrieving step count data. This API is accountless,\nmeaning it does not require a Google Account to use the service, and data is\nstored on-device.\n| **Note:** The Recording API on mobile is a replacement for the Google Fit Android API, which is deprecated.\n\nThis guide shows you how to use the Recording API on mobile in your health \\&\nfitness experiences.\n\nSee the [Recording API on mobile sample](https://github.com/android/health-samples/tree/main/recording-api-on-mobile/RecordingApiOnMobileSample) on GitHub for an\nexample.\n\nNotable details\n---------------\n\nThere are several notable features unique to the Recording API on mobile:\n\n- Once the recording subscription starts or is renewed, data since the latest subscription - for up to 10 days - is accessible.\n- Data is only available when there is an active subscription. If a subscription is removed by calling `unsubscribe`, collected data won't be accessible.\n\nData types\n----------\n\nThe Recording API on mobile can record the following data types:\n\n- [`TYPE_STEP_COUNT_DELTA`](https://developers.google.com/android/reference/com/google/android/gms/fitness/data/LocalDataType#TYPE_STEP_COUNT_DELTA)\n- [`TYPE_DISTANCE_DELTA`](https://developers.google.com/android/reference/com/google/android/gms/fitness/data/LocalDataType#TYPE_DISTANCE_DELTA)\n- [`TYPE_CALORIES_EXPENDED`](https://developers.google.com/android/reference/com/google/android/gms/fitness/data/LocalDataType#TYPE_CALORIES_EXPENDED)\n\nGet Started\n-----------\n\nTo get started, add the following dependency in your `build.gradle` file: \n\n### Kotlin DSL\n\n plugin {\n id(\"com.android.application\")\n }\n\n ...\n\n dependencies {\n implementation(\"com.google.android.gms:play-services-fitness:21.2.0\")\n }\n\n### Groovy DSL\n\n apply plugin: 'com.android.application'\n\n ...\n\n dependencies {\n implementation 'com.google.android.gms:play-services-fitness:21.2.0'\n }\n\n### Request permissions\n\nTo record data using the Recording API on mobile, your app will need to [request\nthe following permission](/training/permissions/requesting#workflow_for_requesting_permissions):\n\n- `android.permission.ACTIVITY_RECOGNITION`\n\nPerform a Play Services version check\n-------------------------------------\n\nTo use the Recording API on mobile, the user must have Google Play services\nupdated to `LOCAL_RECORDING_CLIENT_MIN_VERSION_CODE`. You can check for this\nusing the [`isGooglePlayServicesAvailable`](https://developers.google.com/android/reference/com/google/android/gms/common/GoogleApiAvailability#public-int-isgoogleplayservicesavailable-context-context,-int-minapkversion) method: \n\n val hasMinPlayServices = isGooglePlayServicesAvailable(context, LocalRecordingClient.LOCAL_RECORDING_CLIENT_MIN_VERSION_CODE)\n\n if(hasMinPlayServices != ConnectionResult.SUCCESS) {\n // Prompt user to update their device's Google Play services app and return\n }\n\n // Continue with Recording API functions\n\nOtherwise, if the user's Google Play services version is too low, the system\nthrows a [`ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED`](https://developers.google.com/android/reference/com/google/android/gms/common/ConnectionResult#SERVICE_VERSION_UPDATE_REQUIRED)\nexception.\n\nSubscribe to Fitness Data\n-------------------------\n\nTo request background collection of steps data, use the\n`subscribe` method, as shown in the following code snippet: \n\n val localRecordingClient = FitnessLocal.getLocalRecordingClient(this)\n // Subscribe to steps data\n localRecordingClient.subscribe(LocalDataType.TYPE_STEP_COUNT_DELTA)\n .addOnSuccessListener {\n Log.i(TAG, \"Successfully subscribed!\")\n }\n .addOnFailureListener { e -\u003e\n Log.w(TAG, \"There was a problem subscribing.\", e)\n }\n\nRead and Process Fitness Data\n-----------------------------\n\nOnce subscribed, request the data using the `readData` method. Then, you can\nobtain LocalDataPoints from the resulting [`LocalDataSet`](https://developers.google.com/android/reference/com/google/android/gms/fitness/data/LocalDataSet) by\nmaking a [`LocalDataReadRequest`](https://developers.google.com/android/reference/com/google/android/gms/fitness/request/LocalDataReadRequest), as shown in the following code\nsnippet: \n\n val endTime = LocalDateTime.now().atZone(ZoneId.systemDefault())\n val startTime = endTime.minusWeeks(1)\n val readRequest =\n LocalDataReadRequest.Builder()\n // The data request can specify multiple data types to return,\n // effectively combining multiple data queries into one call.\n // This example demonstrates aggregating only one data type.\n .aggregate(LocalDataType.TYPE_STEP_COUNT_DELTA)\n // Analogous to a \"Group By\" in SQL, defines how data should be\n // aggregated. bucketByTime allows bucketing by time span.\n .bucketByTime(1, TimeUnit.DAYS)\n .setTimeRange(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)\n .build()\n\n localRecordingClient.readData(readRequest).addOnSuccessListener { response -\u003e\n // The aggregate query puts datasets into buckets, so flatten into a\n // single list of datasets.\n for (dataSet in response.buckets.flatMap { it.dataSets }) {\n dumpDataSet(dataSet)\n }\n }\n .addOnFailureListener { e -\u003e\n Log.w(TAG,\"There was an error reading data\", e)\n }\n\n fun dumpDataSet(dataSet: LocalDataSet) {\n Log.i(TAG, \"Data returned for Data type: ${dataSet.dataType.name}\")\n for (dp in dataSet.dataPoints) {\n Log.i(TAG,\"Data point:\")\n Log.i(TAG,\"\\tType: ${dp.dataType.name}\")\n Log.i(TAG,\"\\tStart: ${dp.getStartTime(TimeUnit.HOURS)}\")\n Log.i(TAG,\"\\tEnd: ${dp.getEndTime(TimeUnit.HOURS)}\")\n for (field in dp.dataType.fields) {\n Log.i(TAG,\"\\tLocalField: ${field.name.toString()} LocalValue: ${dp.getValue(field)}\")\n }\n }\n }\n\nThe `LocalRecordingClient` continuously updates its collection of data. You can\nuse `readData` to pull the latest numbers at any time.\n\nNote that the `LocalRecordingClient` stores up to 10 days of data. To reduce the\nrisk of losing data, you can use WorkManager to periodically collect the data in\nthe background.\n\nUnsubscribe from fitness data\n-----------------------------\n\nIn order to free up resources, you should make sure to unsubscribe from the\ncollection of sensor data when your app is no longer in need of it. To\nunsubscribe, use the `unsubscribe` method: \n\n val localRecordingClient = FitnessLocal.getLocalRecordingClient(this)\n // Unsubscribe from steps data\n localRecordingClient.unsubscribe(LocalDataType.TYPE_STEP_COUNT_DELTA)\n .addOnSuccessListener {\n Log.i(TAG, \"Successfully unsubscribed!\")\n }\n .addOnFailureListener { e -\u003e\n Log.w(TAG, \"There was a problem unsubscribing.\", e)\n }"]]