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

Java版Android集成Google Health Connect API计步功能遇阻求助

在Java Android应用中集成Google Health Connect API计步功能的问题

我正在用Java开发Android原生应用,想要集成Google Health Connect API实现计步功能,但遇到了问题——无法将用Kotlin编写的Google Health Connect相关方法整合到Java项目的Fragment与Activity中。

我已经按照Google官方教程《Get started with Health Connect》的步骤操作,还修改了官方提供的代码,但仍然没能成功。

有没有开发者具备在Java版Android应用中集成这款Google新API的经验?

我的Kotlin代码:HealthConnectManager

import android.content.Context
import android.util.Log
import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.aggregate.AggregateMetric
import androidx.health.connect.client.aggregate.AggregationResultGroupedByPeriod
import androidx.health.connect.client.permission.HealthPermission
import androidx.health.connect.client.records.ExerciseSessionRecord
import androidx.health.connect.client.records.SleepSessionRecord
import androidx.health.connect.client.records.StepsRecord
import androidx.health.connect.client.request.AggregateGroupByPeriodRequest
import androidx.health.connect.client.time.TimeRangeFilter.Companion.between
import com.google.android.gms.fitness.data.DataType
import kotlinx.coroutines.CoroutineScope
import java.time.Duration.between
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.Period
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.util.TimeZone
import kotlin.time.Duration

data class DataRecord(
    val metricValue: String,
    val dataType: DataType,
    val fromDatetime: String,
    val toDatetime: String
)

class HealthConnectManager(private val context: Context)  {
        private var healthConnectClient: HealthConnectClient? = null
        val PERMISSIONS = setOf(
            HealthPermission.getReadPermission(StepsRecord::class),
        )

        private val dateTimeFormatter: DateTimeFormatter =
            DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX")

    fun checkForHealthConnectInstalled(context: Context): Int {
        val availabilityStatus =
            HealthConnectClient.getSdkStatus(context, "com.google.android.apps.healthdata")
        when (availabilityStatus) {
            HealthConnectClient.SDK_UNAVAILABLE -> {
                Log.w("HealthConnectUtils", "Health Connect SDK is unavailable on this device.")
            }
            HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED -> {
                Log.w("HealthConnectUtils", "Health Connect SDK needs an update.")
            }
            HealthConnectClient.SDK_AVAILABLE -> {
                Log.i("HealthConnectUtils", "Health Connect SDK is available.")
                healthConnectClient = HealthConnectClient.getOrCreate(context)
            }
        }
        return availabilityStatus
    }

        suspend fun checkPermissions(): Boolean {
            val granted = healthConnectClient?.permissionController?.getGrantedPermissions()
            return granted?.containsAll(PERMISSIONS) == true
        }

        suspend fun readStepsForInterval(interval: Long): List<DataRecord> {
            return readAggregateDataForInterval(interval, DataType.TYPE_STEP_COUNT_DELTA, StepsRecord.COUNT_TOTAL)
        }

        private suspend fun <T : Any> readAggregateDataForInterval(interval: Long, dataType: DataType, metric: AggregateMetric<T>): List<DataRecord> {
            val startTime: ZonedDateTime = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).minusDays(interval - 1)
            val endTime = LocalDateTime.now().atZone(TimeZone.getDefault().toZoneId()).minusMinutes(1).plusSeconds(59)
            val response = healthConnectClient?.aggregateGroupByPeriod(
                AggregateGroupByPeriodRequest(
                    metrics = setOf(metric),
                    timeRangeFilter = between(startTime.toLocalDate().atStartOfDay(), endTime.toLocalDateTime()),
                    timeRangeSlicer = Period.ofDays(1)
                )
            )
            return parseAggregateResponse(response, startTime, endTime, dataType, metric)
        }

        private fun <T : Any> parseAggregateResponse(
            response: List<AggregationResultGroupedByPeriod>?,
            startTime: ZonedDateTime,
            endTime: ZonedDateTime,
            dataType: DataType,
            metric: AggregateMetric<T>
        ): List<DataRecord> {
            val data = mutableListOf<DataRecord>()
            if (response != null) {
                response.sortedBy { it.startTime }
                var trackTime = startTime.toLocalDate().atStartOfDay()
                for (dailyResult in response) {
                    if (dailyResult.startTime.isAfter(trackTime)) {
                        while (trackTime.isBefore(dailyResult.startTime)) {
                            data.add(
                                DataRecord(
                                    metricValue = "No data",
                                    dataType = dataType,
                                    toDatetime = trackTime.toLocalDate().atTime(LocalTime.MAX)
                                        .atZone(ZoneId.systemDefault()).format(dateTimeFormatter),
                                    fromDatetime = if (trackTime.toLocalDate().isEqual(startTime.toLocalDate()))
                                        startTime.format(dateTimeFormatter) else trackTime.atZone(ZoneId.systemDefault()).format(dateTimeFormatter)
                                )
                            )
                            trackTime = trackTime.plusDays(1)
                        }
                    }
                    val totalValue = dailyResult.result[metric]
                    data.add(
                        DataRecord(
                            metricValue = (totalValue ?: "No data").toString(),
                            dataType = dataType,
                            toDatetime = dailyResult.endTime.atZone(ZoneId.systemDefault()).minusSeconds(1).format(dateTimeFormatter),
                            fromDatetime = if (dailyResult.startTime.toLocalDate().isEqual(startTime.toLocalDate()))
                                startTime.format(dateTimeFormatter) else dailyResult.startTime.atZone(ZoneId.systemDefault()).format(dateTimeFormatter)
                        )
                    )
                    trackTime = dailyResult.endTime
                }
                while (trackTime.isBefore(endTime.toLocalDateTime()) && between(trackTime, endTime).toMinutes() > 1) {
                    data.add(
                        DataRecord(
                            metricValue = "No data",
                            dataType = dataType,
                            toDatetime = if (trackTime.toLocalDate().isEqual(endTime.toLocalDate()))
                                endTime.format(dateTimeFormatter) else trackTime.toLocalDate().atTime(LocalTime.MAX)
                                    .atZone(ZoneId.systemDefault()).format(dateTimeFormatter),
                            fromDatetime = if (trackTime.toLocalDate().isEqual(startTime.toLocalDate()))
                                startTime.format(dateTimeFormatter) else trackTime.atZone(ZoneId.systemDefault()).format(dateTimeFormatter)
                        )
                    )
                    trackTime = trackTime.plusDays(1).toLocalDate().atStartOfDay()
                }
            }
            Log.d("HealthConnectUtils", "Parsed Data: $data")
            return data
        }


        suspend fun writeStepsData(context: Context, steps: Int, startTime: ZonedDateTime, endTime: ZonedDateTime) {
            val stepsRecord = StepsRecord(
                count = steps.toLong(),
                startTime = startTime.toInstant(),
                startZoneOffset = startTime.offset,
                endTime = endTime.toInstant(),
                endZoneOffset = endTime.offset
            )

            val response = healthConnectClient?.insertRecords(listOf(stepsRecord))

            if (response != null) {
                Log.d("HealthConnectUtils", "Steps data written successfully: $response")
            } else {
                Log.e("HealthConnectUtils", "Failed to write steps data.")
            }
        }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 10:37:03