Wear OS表盘开发:如何从Google Fit获取步数数据?
解决Wear OS表盘通过Google Fit API获取步数的问题
核心问题分析
你遇到的com.google.android.gms.common.api.ApiException: 4错误,本质是用户未完成Google账号的登录与Fit API授权,而非单纯的系统权限问题。Google Fit API需要双重验证:
- 系统权限(如
ACTIVITY_RECOGNITION、BODY_SENSORS) - Google账号的Fit API访问授权
同时你当前的权限请求代码和服务启动Activity的逻辑存在错误,需要针对性修正。
步骤1:修正权限请求逻辑
1.1 修复PermissionRequestActivity的错误
你的代码中传递权限的方式有误,arrayOf(arrayOf(...).toString())会把权限字符串转成数组的文本表示,导致请求的权限无效。修改如下:
class PermissionRequestActivity : Activity() { private var mPermissions: Array<String>? = null private var mRequestCode: Int = 0 companion object { private const val PERMISSIONS_CODE = 100 const val EXTRA_PERMISSIONS = "EXTRA_PERMISSIONS" } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 正确接收权限数组 mPermissions = intent.getStringArrayExtra(EXTRA_PERMISSIONS) mRequestCode = intent.getIntExtra("KEY_REQUEST_CODE", PERMISSIONS_CODE) mPermissions?.let { ActivityCompat.requestPermissions(this, it, mRequestCode) } ?: finish() } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<String>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == mRequestCode) { permissions.forEachIndexed { index, permission -> val result = if (grantResults[index] == PackageManager.PERMISSION_GRANTED) "granted" else "revoked" Log.d("PermissionRequestActivity", "$permission $result") } } finish() } }
1.2 在CanvasWatchFaceService中正确启动权限请求Activity
服务中启动Activity必须添加FLAG_ACTIVITY_NEW_TASK,同时传递正确的权限数组(获取步数需要ACTIVITY_RECOGNITION和BODY_SENSORS):
// 建议通过表盘长按触发的配置入口启动,而非直接在onCreate中启动,避免打扰用户 val intent = Intent(this, PermissionRequestActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) putStringArrayExtra(PermissionRequestActivity.EXTRA_PERMISSIONS, arrayOf( Manifest.permission.ACTIVITY_RECOGNITION, Manifest.permission.BODY_SENSORS )) } startActivity(intent)
步骤2:完成Google Sign-In与Fit API授权
这是解决错误4的核心,必须让用户登录Google账号并授权Fit API访问:
2.1 定义FitnessOptions
private val fitnessOptions = FitnessOptions.builder() .addDataType(DataType.TYPE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ) .addDataType(DataType.AGGREGATE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ) .build()
2.2 检查登录状态并发起授权
在表盘的配置Activity(或PermissionRequestActivity中)添加登录检查逻辑:
private const val REQUEST_CODE_FIT_SIGN_IN = 200 // 检查是否已登录并授权 val account = GoogleSignIn.getLastSignedInAccount(context) if (account == null || !GoogleSignIn.hasPermissions(account, fitnessOptions)) { // 发起登录授权流程 val signInIntent = GoogleSignIn.getSignInIntent(fitnessOptions) startActivityForResult(signInIntent, REQUEST_CODE_FIT_SIGN_IN) } else { // 已授权,可调用Fit API获取步数 fetchStepCount(account) } // 处理登录授权结果 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_CODE_FIT_SIGN_IN) { val task = GoogleSignIn.getSignedInAccountFromIntent(data) try { val account = task.getResult(ApiException::class.java) // 授权成功,开始获取步数 fetchStepCount(account) } catch (e: ApiException) { Log.e("FitAuth", "登录授权失败: ${e.statusCode}") } } }
步骤3:修正GoogleFitHelper的逻辑
确保调用Fit API前,账号已完成授权:
class GoogleFitHelper(private val context: Context) { private val fitnessOptions = FitnessOptions.builder() .addDataType(DataType.TYPE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ) .addDataType(DataType.AGGREGATE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ) .build() fun getStepCount(callback: (Int) -> Unit) { val account = GoogleSignIn.getLastSignedInAccount(context) if (account == null || !GoogleSignIn.hasPermissions(account, fitnessOptions)) { callback(0) Log.e("FitHelper", "用户未登录或未授权Fit API") return } val endTime = System.currentTimeMillis() val startTime = endTime - TimeUnit.DAYS.toMillis(1) val readRequest = DataReadRequest.Builder() .read(DataType.TYPE_STEP_COUNT_DELTA) .setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS) .aggregate(DataType.TYPE_STEP_COUNT_DELTA, DataType.AGGREGATE_STEP_COUNT_DELTA) .bucketByTime(1, TimeUnit.DAYS) // 按天聚合数据 .build() Fitness.getHistoryClient(context, account) .readData(readRequest) .addOnSuccessListener { response -> val stepCount = processStepCountData(response) callback(stepCount) } .addOnFailureListener { exception -> Log.d("step_fit", "获取步数失败: $exception") callback(0) } } private fun processStepCountData(response: DataReadResponse): Int { var totalSteps = 0 for (bucket in response.buckets) { for (dataSet in bucket.dataSets) { totalSteps += dataSet.dataPoints.sumOf { dp -> dp.getValue(Field.FIELD_STEPS).asInt() } } } return totalSteps } }
步骤4:配置AndroidManifest.xml
确保添加必要的权限和组件声明:
<!-- 系统权限 --> <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" /> <uses-permission android:name="android.permission.BODY_SENSORS" /> <uses-permission android:name="android.permission.INTERNET" /> <!-- 声明PermissionRequestActivity --> <activity android:name=".PermissionRequestActivity" android:exported="true" android:theme="@android:style/Theme.Translucent.NoTitleBar" /> <!-- 声明表盘服务 --> <service android:name=".VpetWatchFace" android:exported="true" android:permission="android.permission.BIND_WALLPAPER"> <meta-data android:name="com.google.android.wearable.watchface.preview" android:resource="@drawable/preview" /> <intent-filter> <action android:name="android.service.wallpaper.WallpaperService" /> <category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" /> </intent-filter> </service>
关键注意事项
- 避免在服务中直接弹出授权页面:建议通过表盘的配置入口引导用户完成授权,避免后台弹窗影响体验。
- 实时检查授权状态:用户可能在系统设置中取消授权,每次调用Fit API前都要验证授权状态。
- 独立Wear OS应用配置:如果是独立Wear应用,需在Google API控制台正确配置Wear OS的OAuth 2.0客户端ID。
内容的提问来源于stack exchange,提问作者Blodht
相关产品推荐
相关产品推荐

