app development
Android
crash debugging
window manager
programming error

View not attached to window manager crash

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The Android crash described as "View not attached to window manager" usually means UI code is trying to dismiss, update, or remove something after it has already left the active window hierarchy. The stack trace often points at dialogs or popups, but the real bug is almost always a lifecycle or ownership problem in application code.

Core Sections

What the crash actually means

A View, Dialog, or PopupWindow can only interact with the WindowManager while it is attached to a valid window. Once the activity is finishing, the fragment view is destroyed, or the popup has already been dismissed, that attachment is gone.

A common pattern looks harmless at first:

kotlin
1class MainActivity : AppCompatActivity() {
2    private var progressDialog: AlertDialog? = null
3
4    fun loadData() {
5        progressDialog = AlertDialog.Builder(this)
6            .setMessage("Loading...")
7            .create()
8        progressDialog?.show()
9
10        lifecycleScope.launch {
11            delay(3000)
12            progressDialog?.dismiss()
13        }
14    }
15}

If the user leaves the screen before the coroutine resumes, the dialog may no longer be attached when dismiss() runs. The crash is therefore about timing, not about AlertDialog being broken.

The underlying cause is usually lifecycle drift

This issue appears when asynchronous work outlives the UI element it wants to control. Common sources are:

  • network callbacks that return after navigation
  • delayed handlers or posted runnables
  • coroutines launched in the wrong scope
  • fragment instances holding references to destroyed views
  • multiple code paths dismissing the same transient UI element

Once you recognize the pattern, the fix becomes clearer: tie the work to the UI lifecycle and stop storing stale UI references.

Defensive checks help, but they are not the full fix

When you must dismiss a dialog manually, check both the dialog state and the activity state.

kotlin
1private fun safeDismiss(dialog: AlertDialog?) {
2    if (dialog != null && dialog.isShowing && !isFinishing && !isDestroyed) {
3        dialog.dismiss()
4    }
5}

That prevents many common crashes. It is still a guardrail, not a full design solution. If the same dialog reference is shared across long-lived callbacks, you can still create races or double-dismiss behavior.

For fragments, remember that the fragment instance may still exist even though its view has been destroyed. That is why view binding cleanup and viewLifecycleOwner matter.

Prefer lifecycle-aware UI updates

Modern Android code is safer when background work exposes state and the activity or fragment renders that state only while it is active.

kotlin
1viewLifecycleOwner.lifecycleScope.launch {
2    repeatOnLifecycle(Lifecycle.State.STARTED) {
3        viewModel.loading.collect { isLoading ->
4            binding.progressBar.isVisible = isLoading
5        }
6    }
7}

This approach removes the need for repositories or long-running callbacks to hold dialog references. The UI layer stays responsible for its own window-attached elements, and collection automatically stops when the view lifecycle drops below the requested state.

Avoid long-lived references to views and dialogs

A strong smell in Android code is a singleton, repository, adapter, or background task that holds a direct reference to a View, Dialog, or Context that belongs to a screen. Those objects have shorter lifetimes than the background work that often references them.

A safer architecture is to pass data and events upward, then let the current screen decide whether it is still able to show or dismiss UI.

kotlin
1class LoadingViewModel : ViewModel() {
2    private val _loading = MutableStateFlow(false)
3    val loading: StateFlow<Boolean> = _loading
4
5    suspend fun refresh() {
6        _loading.value = true
7        try {
8            delay(1000)
9        } finally {
10            _loading.value = false
11        }
12    }
13}

With this pattern, there is no background object attempting to manipulate the window manager directly.

Where to look in a real crash investigation

If the crash references window-manager internals, search backward from the stack trace for the callback that triggered the UI action. Most fixes come from one of these changes:

  • move the work to a lifecycle-aware scope
  • cancel delayed work when the screen closes
  • clear view references in onDestroyView
  • centralize dialog ownership inside the current UI controller
  • replace imperative dialog toggling with state-driven rendering

The window-manager line in the stack trace is usually the symptom, not the cause.

Common Pitfalls

  • Adding only a null check around a dialog reference is insufficient because a non-null dialog can still be detached.
  • Wrapping dismiss() in a broad try and catch hides the race instead of fixing the ownership problem.
  • Launching work in a scope that outlives the fragment view often causes callbacks to target stale bindings or dialogs.
  • Holding a View or Context in lower application layers creates attachment bugs and memory leaks at the same time.
  • Treating the crash as a widget issue instead of a lifecycle issue leads to repeated regressions in different screens.

Summary

  • The crash occurs when code touches a UI object after it is no longer attached to a valid window.
  • Dialog dismissals and popup updates after navigation are common triggers.
  • Defensive state checks help, but lifecycle-aware state-driven UI is the stronger fix.
  • Fragment view lifetime is shorter than fragment lifetime, so stale references are a frequent source of the problem.
  • Debug the asynchronous ownership path that led to the call, not only the line where the window manager throws.

Course illustration
Course illustration

All Rights Reserved.