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

Kotlin实现悬浮窗服务时抛出Unable to add window -- token null is not valid; is your activity running?错误

Kotlin实现悬浮窗服务时抛出Unable to add window -- token null is not valid; is your activity running?错误

嘿,我刚看完你的代码和错误日志,这个问题其实挺常见的,核心原因是你窗口类型参数设置错误,再加上权限校验没做全导致的。咱们一步步来解决:

错误原因分析

你代码里明明定义了LAYOUT_FLAG来根据Android版本选择正确的窗口类型(O以上用TYPE_APPLICATION_OVERLAY,低版本用TYPE_PHONE),但实际创建WindowManager.LayoutParams的时候,却硬写了WindowManager.LayoutParams.TYPE_APPLICATION_PANEL——这个类型是给Activity内部窗口用的,需要绑定Activity的token,而Service本身没有Activity的token,自然就抛出"token null is not valid"的错误了。

另外,SYSTEM_ALERT_WINDOW权限在Android 6.0+的处理比较特殊,光在Manifest里声明还不够,必须引导用户去系统设置里手动开启权限,否则悬浮窗也无法正常显示。

解决方案

1. 修正窗口类型参数

把创建WindowManager.LayoutParams时的类型替换成你已经定义好的LAYOUT_FLAG,替换这行代码:

val parameters = WindowManager.LayoutParams(
    ViewGroup.LayoutParams.WRAP_CONTENT,
    ViewGroup.LayoutParams.WRAP_CONTENT,
    // 把这里的TYPE_APPLICATION_PANEL改成LAYOUT_FLAG
    LAYOUT_FLAG,
    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
    PixelFormat.TRANSLUCENT
)

2. 添加权限校验逻辑

在启动悬浮窗服务之前,先检查是否已经获取了悬浮窗权限:

  • 在MainActivity中,启动服务前先判断权限:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    if (!Settings.canDrawOverlays(this)) {
        // 引导用户去设置页开启权限
        val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName"))
        startActivityForResult(intent, 1001)
    } else {
        // 权限已获取,启动服务
        startService(Intent(this, FloatingWindow::class.java))
    }
} else {
    // 低版本直接启动服务
    startService(Intent(this, FloatingWindow::class.java))
}
  • 然后在MainActivity中处理权限申请的结果:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == 1001) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (Settings.canDrawOverlays(this)) {
                startService(Intent(this, FloatingWindow::class.java))
            } else {
                Toast.makeText(this, "需要开启悬浮窗权限才能使用", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

3. 优化Service中的空安全处理

你的代码里用了很多!!强制非空调用,容易触发空指针,建议改成安全调用?.:
比如把wm!!.addView(ll, parameters)改成wm?.addView(ll, parameters),同理其他地方的!!都替换成?.,或者提前做空判断。

修改后的FloatingWindow关键代码片段

@RequiresApi(Build.VERSION_CODES.O)
@SuppressLint("ClickableViewAccessibility")
override fun onCreate() {
    super.onCreate()
    wm = getSystemService(Context.WINDOW_SERVICE) as WindowManager?
    ll = LinearLayout(this@FloatingWindow).apply {
        setBackgroundColor(Color.WHITE)
        layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 400)
        orientation = LinearLayout.VERTICAL
    }

    val LAYOUT_FLAG = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
    } else {
        WindowManager.LayoutParams.TYPE_PHONE
    }

    val parameters = WindowManager.LayoutParams(
        ViewGroup.LayoutParams.WRAP_CONTENT,
        ViewGroup.LayoutParams.WRAP_CONTENT,
        LAYOUT_FLAG, // 这里用正确的窗口类型
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
        PixelFormat.TRANSLUCENT
    ).apply {
        gravity = Gravity.TOP or Gravity.END
        x = 0
        y = 0
    }

    // ... 后面的Button、TextView初始化代码不变 ...

    // 安全调用添加View
    wm?.addView(ll, parameters)

    // ... 触摸监听和点击事件代码不变 ...
}

这样修改之后,应该就能解决你遇到的错误了。核心就是用对窗口类型,确保权限到位,再做好空安全处理~

备注:内容来源于stack exchange,提问作者Wanjala Alex

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 14:52:37