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

Health Connect:async协程中调用getGrantedPermissions()致进程冻结

问题分析与解决方案

问题根源

你遇到的进程冻结/ANR问题,核心是错误的协程使用逻辑加上getGrantedPermissions()内部的线程调度冲突:

  • 原代码用GlobalScope.async嵌套包裹挂起函数,再通过await()等待结果,这种操作完全多余,还可能导致任务被分发到与runBlocking阻塞线程冲突的调度器上,引发死锁。
  • 另外,getGrantedPermissions()可能依赖权限请求Intent初始化的内部状态,未发起权限请求时,该函数内部可能存在未正确处理的阻塞逻辑。

修正后的代码实现

直接在runBlocking中调用挂起函数即可,无需额外套async——runBlocking本身就是官方用来桥接阻塞代码与挂起函数的工具:

private val PERMISSIONS = setOf(HealthPermission.getReadPermission(StepsRecord::class))
private fun syncCheckPermissions(healthConnectClient: HealthConnectClient): Boolean {
    return runBlocking {
        try {
            withTimeout(5000) {
                // 直接在runBlocking上下文调用挂起函数,避免调度冲突
                val grantedPermissions = healthConnectClient.permissionController.getGrantedPermissions()
                grantedPermissions.containsAll(PERMISSIONS)
            }
        } catch (e: TimeoutCancellationException) {
            true // 按原需求超时返回true,可根据实际场景调整
        }
    }
}

额外优化建议

  1. 禁止在UI线程调用runBlocking:如果这个函数在UI线程执行,强行阻塞会直接触发ANR,建议改成挂起函数,绑定到组件生命周期的协程作用域调用:
private suspend fun checkPermissions(healthConnectClient: HealthConnectClient): Boolean {
    return try {
        withTimeout(5000) {
            val grantedPermissions = healthConnectClient.permissionController.getGrantedPermissions()
            grantedPermissions.containsAll(PERMISSIONS)
        }
    } catch (e: TimeoutCancellationException) {
        true
    }
}
  1. 弃用GlobalScope:它的生命周期不受控,容易引发内存泄漏或调度异常,优先使用lifecycleScope、viewModelScope这类绑定组件生命周期的协程作用域。

关于“从挂起函数获取非挂起结果”的通用方案

如果必须在非挂起函数中调用挂起函数,runBlocking是Kotlin官方唯一推荐的桥接方式,但要注意:

  • 绝对不要在UI线程使用runBlocking,否则必然阻塞界面
  • 不要嵌套async/await,直接在runBlocking块内调用挂起函数即可,多余的协程创建只会增加调度风险

内容的提问来源于stack exchange,提问作者A. Ferrand

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 20:37:44