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
AsyncTaskfinishes after the user already pressed Back. The dialog tries to show on a destroyed Activity. - Calling
dialog.show()afterfinish()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 theIllegalStateExceptioncrash.
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
BadTokenExceptionrate should drop to zero within a day or two. - Run
adb logcat | grep BadTokenExceptionwhile 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()beforedialog.show() - Never use:
getApplicationContext()as the dialog context - Prefer:
lifecycleScope.launchover 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

