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,可根据实际场景调整 } } }
额外优化建议
- 禁止在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 } }
- 弃用
GlobalScope:它的生命周期不受控,容易引发内存泄漏或调度异常,优先使用lifecycleScope、viewModelScope这类绑定组件生命周期的协程作用域。
关于“从挂起函数获取非挂起结果”的通用方案
如果必须在非挂起函数中调用挂起函数,runBlocking是Kotlin官方唯一推荐的桥接方式,但要注意:
- 绝对不要在UI线程使用
runBlocking,否则必然阻塞界面 - 不要嵌套
async/await,直接在runBlocking块内调用挂起函数即可,多余的协程创建只会增加调度风险
内容的提问来源于stack exchange,提问作者A. Ferrand
相关产品推荐
相关产品推荐

