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

createWindowContext创建失败:未使用上下文过多的解决方案咨询

解决createWindowContext failed! Too many unused window contexts崩溃问题

问题背景

收到如下崩溃报错:

java.lang.UnsupportedOperationException: createWindowContext failed! 
Too many unused window contexts. Please see Context createWindowContext documentation for detail.

查阅Context#createWindowContext文档说明:

创建窗口上下文是一项开销较大的操作。误用该API可能导致性能大幅下降。最佳实践是尽可能复用同一窗口上下文,即针对特定窗口类型和显示器创建一个窗口上下文,并在所有需要的地方复用。

业务场景是:应用退至后台时启动服务,服务通过createWindowContext获取上下文显示悬浮窗;短时间内多次启停应用时,每次启动服务都调用createWindowContext,最终触发该崩溃。

目前尝试过将创建好的windowContext存储在服务的companion object中,测试可行但担心存在隐患,也考虑过用WeakReference在companion object中存储windowContext。

现有方案的隐患

  • 直接存在companion object(静态单例):如果服务被销毁但静态引用未释放,会导致Context内存泄漏,长期占用系统资源,甚至引发其他内存相关问题。
  • WeakReference方案:虽能避免内存泄漏,但如果GC回收了WeakReference中的Context,下次使用时需要重新创建,仍可能遇到短时间多次创建的问题,还需额外处理空值逻辑。

更优解决方案

1. 服务内复用+主动释放

窗口上下文是和服务绑定用于显示悬浮窗的,最佳方式是在服务内部维护实例,服务生命周期内复用,销毁时主动释放资源:

  • 在服务类中用成员变量存储windowContext,而非静态companion object
  • 仅在onCreate中创建一次,后续直接复用
  • onDestroy中移除悬浮窗后,将windowContext置为null,允许GC回收

优化后的示例代码:

class FloatingWindowService : Service() {
    private var windowContext: Context? = null
    private var floatingView: View? = null

    override fun onCreate() {
        super.onCreate()
        // 仅首次创建服务时初始化windowContext
        windowContext = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            displayContext().createWindowContext(
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, // 替换为你的WindowType
                null
            )
        } else {
            this
        }
        // 初始化悬浮窗ComposeView
        floatingView = ComposeView(windowContext!!).apply {
            // 填充你的Compose内容
        }
        // 添加到WindowManager
        windowManager.addView(floatingView, layoutParams)
    }

    override fun onDestroy() {
        super.onDestroy()
        // 移除悬浮窗
        floatingView?.let { windowManager.removeView(it) }
        // 释放上下文及View引用
        windowContext = null
        floatingView = null
    }

    private fun Context.displayContext(): Context {
        val manager = getSystemService(DISPLAY_SERVICE) as DisplayManager
        val display = manager.getDisplay(Display.DEFAULT_DISPLAY)
        return createDisplayContext(display)
    }

    // ... 其他服务逻辑
}

2. 全局单例管理(带生命周期感知)

如果多个组件需要复用同一窗口上下文,可以创建全局单例管理器,配合Application生命周期管理Context的创建与释放:

object WindowContextManager {
    private var windowContext: Context? = null

    fun getWindowContext(baseContext: Context): Context {
        // 检查上下文是否有效,无效则重新创建
        if (windowContext == null || windowContext!!.applicationContext !== baseContext.applicationContext) {
            windowContext = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                baseContext.displayContext().createWindowContext(
                    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                    null
                )
            } else {
                baseContext
            }
        }
        return windowContext!!
    }

    // 全局释放上下文
    fun release() {
        windowContext = null
    }

    private fun Context.displayContext(): Context {
        val manager = getSystemService(DISPLAY_SERVICE) as DisplayManager
        val display = manager.getDisplay(Display.DEFAULT_DISPLAY)
        return createDisplayContext(display)
    }
}

在Application的onTerminate方法中调用WindowContextManager.release(),或者在所有使用该上下文的组件销毁时统一执行释放操作。

3. WeakReference折中方案

如果担心全局单例内存泄漏,又想复用上下文,可以用WeakReference存储,获取时检查有效性,无效则重新创建:

object WindowContextManager {
    private var windowContextRef: WeakReference<Context>? = null

    fun getWindowContext(baseContext: Context): Context {
        val existingContext = windowContextRef?.get()
        return if (existingContext != null && existingContext.applicationContext === baseContext.applicationContext) {
            existingContext
        } else {
            val newContext = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                baseContext.displayContext().createWindowContext(
                    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                    null
                )
            } else {
                baseContext
            }
            windowContextRef = WeakReference(newContext)
            newContext
        }
    }
}

这种方式既能复用上下文,又能在无引用时被GC回收,只要应用进程未被杀死,WeakReference中的Context大概率不会被回收,能有效避免短时间多次创建的问题。

关键注意事项

  • 必须确保窗口上下文和悬浮窗View一起释放,避免内存泄漏
  • 窗口上下文与特定Display、WindowType绑定,若业务支持多显示器或不同窗口类型,需按类型缓存实例,而非单一实例
  • 仅Android R及以上版本需要使用createWindowContext,低版本直接使用服务自身上下文即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 09:40:25