Fix android.view.WindowManager$BadTokenException When Showing Dialog on Android

intermediate๐Ÿ“ฑ Android2026-04-22| Android (API 21+), Java/Kotlin, all Android versions including Android 10/11/12/13/14

Error Message

android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@... is not valid; is your activity running?
#android#dialog#activity#lifecycle#windowmanager

The Error

android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@4f3a2c1 is not valid; is your activity running?

This crash typically surfaces in Crashlytics right when a dialog tries to appear. Check the stack trace โ€” it almost always points to dialog.show() buried inside an async callback or a Handler.postDelayed().

Why This Happens

Every dialog in Android needs a window token from its context โ€” normally the Activity. When you call dialog.show(), Android tries to attach the dialog window to that token. Dead Activity = invalid token = crash.

Common culprits:

  • A network callback or AsyncTask finishes after the user already pressed Back. The dialog tries to show on a destroyed Activity.
  • Calling dialog.show() after finish() was already invoked.
  • Passing getApplicationContext() to the dialog constructor โ€” application context has no window token.
  • Screen rotation mid-request: the old Activity is gone, but the callback still holds a stale reference to it.
  • Fragment dialogs shown after onSaveInstanceState() โ€” a close cousin of the IllegalStateException crash.

Step-by-Step Fix

1. Check if the Activity is Still Alive Before Showing

Add this guard before every dialog.show() call:

// Kotlin
fun showDialogSafely(activity: Activity, dialog: AlertDialog) {
    if (!activity.isFinishing && !activity.isDestroyed) {
        dialog.show()
    }
}
// Java
private void showDialogSafely(Activity activity, AlertDialog dialog) {
    if (!activity.isFinishing() && !activity.isDestroyed()) {
        dialog.show();
    }
}

Why both checks? isFinishing() flips to true the moment finish() is called โ€” but the Activity isn't actually gone yet. isDestroyed() covers the fully torn-down state. You need both.

2. Use the Activity Context, Not Application Context

Dialogs need a UI context with a window token. getApplicationContext() doesn't have one โ€” it will always crash:

// WRONG โ€” will crash
AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());

// CORRECT
AlertDialog.Builder builder = new AlertDialog.Builder(MyActivity.this);
// Kotlin:
AlertDialog.Builder(this@MyActivity)

3. Show Dialogs on the Main Thread

Background threads can't touch UI. If your dialog lives inside a callback or async block, bounce it to the main thread first:

// Kotlin โ€” inside a coroutine or callback
runOnUiThread {
    if (!isFinishing && !isDestroyed) {
        AlertDialog.Builder(this)
            .setMessage("Done")
            .show()
    }
}
// Java โ€” with Handler
new Handler(Looper.getMainLooper()).post(() -> {
    if (!isFinishing() && !isDestroyed()) {
        new AlertDialog.Builder(MyActivity.this)
            .setMessage("Done")
            .show();
    }
});

4. Fix the Async/Coroutine Pattern

Manual lifecycle checks are a band-aid. The real fix is tying async work to the Activity's lifecycle so it cancels automatically when the screen closes:

// Kotlin โ€” lifecycleScope cancels when the Activity is destroyed
lifecycleScope.launch {
    val result = withContext(Dispatchers.IO) {
        fetchData() // runs on background thread
    }
    // Back on main thread โ€” Activity is guaranteed alive here
    AlertDialog.Builder(this@MyActivity)
        .setMessage(result)
        .show()
}

No isFinishing() check needed. lifecycleScope cancels itself when the Activity dies โ€” the coroutine never reaches show().

5. For Fragments โ€” Use DialogFragment

Showing a dialog from a Fragment? Don't call dialog.show() directly. Use DialogFragment and verify the Fragment is still attached:

// Check fragment is still attached
if (isAdded && !requireActivity().isFinishing) {
    MyDialogFragment().show(parentFragmentManager, "my_dialog")
}

If you must show it after state is saved, use commitAllowingStateLoss() on the FragmentTransaction โ€” but only when losing the dialog state is acceptable.

6. Dismiss Dialog in onDestroy

Holding a reference to a dialog? Clean it up before the Activity dies, or Android will do it for you โ€” with a crash:

private var progressDialog: AlertDialog? = null

override fun onDestroy() {
    progressDialog?.dismiss()
    progressDialog = null
    super.onDestroy()
}

Verify the Fix

  • Trigger the async operation, then immediately press Back or rotate. No crash = you're done.
  • After deploying, watch Crashlytics โ€” the BadTokenException rate should drop to zero within a day or two.
  • Run adb logcat | grep BadTokenException while hammering edge cases. Silence is the goal.
  • In unit tests, assert that any dialog-showing code path checks Activity state before calling show().

Quick Reference

  • Always check: !activity.isFinishing() && !activity.isDestroyed() before dialog.show()
  • Never use: getApplicationContext() as the dialog context
  • Prefer: lifecycleScope.launch over raw threads or AsyncTask for UI-touching async work
  • Dismiss on: onDestroy() if you hold a dialog reference
  • Always test: screen rotation on any screen that mixes async operations with dialogs

Related Error Notes