You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

关键注意事项

  1. 避免在服务中直接弹出授权页面:建议通过表盘的配置入口引导用户完成授权,避免后台弹窗影响体验。
  2. 实时检查授权状态:用户可能在系统设置中取消授权,每次调用Fit API前都要验证授权状态。
  3. 独立Wear OS应用配置:如果是独立Wear应用,需在Google API控制台正确配置Wear OS的OAuth 2.0客户端ID。

内容的提问来源于stack exchange,提问作者Blodht

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 15:50:21