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

为何用runBlocking阻塞线程会导致悬浮窗无法正常工作?

class MyAccessibilityService : AccessibilityService() {
  override fun onServiceConnected() {
    super.onServiceConnected()
    windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
    showOverlay()
  }

  private fun showOverlay() {   
    // omitted about composeView code
    
    windowManager.addView(composeView, params)

    runBlocking(newSingleThreadContext("MyOwnThread")) {
      while (true) {
        delay(1000 * 10)
        windows.forEach { println(it) }
     }
   }
  }
}

问题:在调用windowManager.addView后使用runBlocking阻塞线程,为何会导致悬浮窗无法正常工作?
我无法理解两者之间存在何种关联?

原因拆解

  • 主线程被永久卡死:onServiceConnected是在AccessibilityService的主线程(UI线程)中执行的,runBlocking的本质是阻塞当前调用它的线程——也就是这个主线程。哪怕你在runBlocking里开了新线程跑循环,runBlocking会一直等待内部协程结束,而你的while(true)是无限循环,导致主线程彻底被堵死。
  • 悬浮窗初始化依赖主线程:windowManager.addView只是把视图提交到窗口系统,后续视图的测量、布局、绘制全要在主线程完成。主线程被卡死,这些关键步骤根本跑不起来,悬浮窗自然无法显示或正常工作。
  • Service核心逻辑瘫痪:AccessibilityService的所有核心回调(比如无障碍事件处理)都依赖主线程,主线程被堵死,整个Service的功能都会失效,悬浮窗作为Service的一部分当然也无法正常运作。

修复方案

把后台循环改成非阻塞的协程方式,不要占用主线程:

private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob())

private fun showOverlay() {   
    // omitted about composeView code
    
    windowManager.addView(composeView, params)

    // 用独立协程跑后台循环,不阻塞主线程
    serviceScope.launch {
        while (isActive) { // 用isActive判断协程状态,避免内存泄漏
            delay(1000 * 10)
            windows.forEach { println(it) }
        }
    }
}

// Service销毁时取消协程,防止内存泄漏
override fun onDestroy() {
    super.onDestroy()
    serviceScope.cancel()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 14:22:07