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

如何在Helper类中优雅架构实现HealthConnect Client?

HealthConnect封装类的优化实现方案

问题1:避免重复实例化HealthConnectClient

原代码中同时使用by lazy初始化和checkAvailability内的重复创建,造成冗余实例。优化思路:

  • 将healthConnectClient改为可空类型,仅在确认HealthConnect可用时初始化
  • 移除lazy初始化逻辑,统一在可用性检查后完成实例创建
  • 对外提供安全的客户端获取方法,附带可用性二次校验避免空指针

优化后核心代码片段

class HealthConnectManager(private val context: Context) {
    private var healthConnectClient: HealthConnectClient? = null

    val PERMISSIONS = setOf(
        HealthPermission.getReadPermission(HeartRateRecord::class),
        HealthPermission.getWritePermission(HeartRateRecord::class),
        HealthPermission.getReadPermission(StepsRecord::class),
        HealthPermission.getWritePermission(StepsRecord::class)
    )

    init {
        checkAvailability()
    }

    private fun checkAvailability() {
        if (HealthConnectClient.isProviderAvailable(context)) {
            healthConnectClient = HealthConnectClient.getOrCreate(context)
        } else {
            Toast.makeText(context, "Health Connect未安装,请前往应用商店下载", Toast.LENGTH_LONG).show()
            val marketIntent = Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.google.android.apps.healthdata"))
            marketIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            context.startActivity(marketIntent)
        }
    }

    // 对外提供安全的客户端获取入口
    fun getClient(): HealthConnectClient? {
        return healthConnectClient.takeIf { HealthConnectClient.isProviderAvailable(context) }
    }
}

问题2:权限处理的优雅实现

权限请求依赖ActivityResultContract与Activity上下文,权限状态检查为挂起函数,分两部分优化:

1. 权限状态检查(hasAllPermissions)

修复原代码中未定义hcClient的问题,添加协程调度优化:

suspend fun hasAllPermissions(permissions: Set<HealthPermission> = PERMISSIONS): Boolean {
    return withContext(Dispatchers.IO) {
        val client = getClient() ?: return@withContext false
        val grantedPermissions = client.permissionController.getGrantedPermissions(permissions)
        permissions.all { grantedPermissions.contains(it) }
    }
}

该函数需在协程作用域(如lifecycleScope.launch)中调用,无需Activity上下文。

2. 权限请求逻辑

由于PermissionController.createRequestPermissionResultContract()必须与Activity的registerForActivityResult绑定,Helper类仅需提供Contract实例和权限集合,由Activity完成注册与请求发起:

Helper类中保留Contract提供方法

fun getPermissionRequestContract(): ActivityResultContract<Set<HealthPermission>, Set<HealthPermission>> {
    return androidx.health.connect.client.PermissionController.createRequestPermissionResultContract()
}

MainActivity中使用示例

class MainActivity : AppCompatActivity() {
    private lateinit var hcManager: HealthConnectManager
    private lateinit var permissionLauncher: androidx.activity.result.ActivityResultLauncher<Set<HealthPermission>>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        hcManager = HealthConnectManager(this)

        // 注册权限请求Launcher
        permissionLauncher = registerForActivityResult(hcManager.getPermissionRequestContract()) { grantedPermissions ->
            lifecycleScope.launch {
                if (hcManager.hasAllPermissions()) {
                    Toast.makeText(this@MainActivity, "所有权限已授予", Toast.LENGTH_SHORT).show()
                    readHealthData()
                } else {
                    Toast.makeText(this@MainActivity, "部分权限未授予,功能受限", Toast.LENGTH_SHORT).show()
                }
            }
        }

        // 检查权限并发起请求
        lifecycleScope.launch {
            if (!hcManager.hasAllPermissions()) {
                permissionLauncher.launch(hcManager.PERMISSIONS)
            } else {
                readHealthData()
            }
        }
    }

    private suspend fun readHealthData() {
        val client = hcManager.getClient() ?: return
        // 此处添加HealthConnect数据读写逻辑,比如读取步数、心率
        Toast.makeText(this, "开始读取健康数据", Toast.LENGTH_SHORT).show()
    }
}

可选优化:Helper类深度封装权限请求(需注意内存泄漏)

若希望Helper类更深度封装权限逻辑,可传入Activity并使用弱引用避免泄漏:

class HealthConnectManager(private val activity: Activity) {
    private val weakActivity = WeakReference(activity)
    private var healthConnectClient: HealthConnectClient? = null

    // ... 其他代码

    fun requestPermissions(onResult: (Set<HealthPermission>) -> Unit) {
        val currentActivity = weakActivity.get() ?: return
        val launcher = currentActivity.registerForActivityResult(getPermissionRequestContract()) {
            onResult(it)
        }
        launcher.launch(PERMISSIONS)
    }
}

此方式需在Activity的onCreate生命周期内调用权限请求方法,避免生命周期不匹配问题。

完整优化后的HealthConnectManager代码

import android.content.Context
import android.content.Intent
import android.net.Uri
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContract
import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.permission.HealthPermission
import androidx.health.connect.client.records.HeartRateRecord
import androidx.health.connect.client.records.StepsRecord
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class HealthConnectManager(private val context: Context) {
    private var healthConnectClient: HealthConnectClient? = null

    val PERMISSIONS = setOf(
        HealthPermission.getReadPermission(HeartRateRecord::class),
        HealthPermission.getWritePermission(HeartRateRecord::class),
        HealthPermission.getReadPermission(StepsRecord::class),
        HealthPermission.getWritePermission(StepsRecord::class)
    )

    init {
        checkAvailability()
    }

    private fun checkAvailability() {
        if (HealthConnectClient.isProviderAvailable(context)) {
            healthConnectClient = HealthConnectClient.getOrCreate(context)
        } else {
            Toast.makeText(context, "Health Connect未安装,请前往应用商店下载", Toast.LENGTH_LONG).show()
            val marketIntent = Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.google.android.apps.healthdata"))
            marketIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            context.startActivity(marketIntent)
        }
    }

    fun getClient(): HealthConnectClient? {
        return healthConnectClient.takeIf { HealthConnectClient.isProviderAvailable(context) }
    }

    suspend fun hasAllPermissions(permissions: Set<HealthPermission> = PERMISSIONS): Boolean {
        return withContext(Dispatchers.IO) {
            val client = getClient() ?: return@withContext false
            val grantedPermissions = client.permissionController.getGrantedPermissions(permissions)
            permissions.all { grantedPermissions.contains(it) }
        }
    }

    fun getPermissionRequestContract(): ActivityResultContract<Set<HealthPermission>, Set<HealthPermission>> {
        return androidx.health.connect.client.PermissionController.createRequestPermissionResultContract()
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 22:21:01