應用退出後重新進入,網絡異常彈窗導致 Crash,日誌拋出:
android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@... is not valid; is your activity running?
Dialog 必須依附於一個存活的 Activity/Window。當 Activity 已 `finish()`、被系統回收、或處於非運行狀態(如 `onStop` 後)時,其 Window Token 失效,此時調用 `dialog.show()` 即拋出該異常。
// 錯誤示範:直接用 MainActivity.this,若 Activity 已銷毀則崩潰
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.create().show();
private void showNetErrorDialog() {
if (isFinishing() || isDestroyed()) return; // API 17+ 用 isDestroyed()
new AlertDialog.Builder(this)
.setTitle("提示")
.setMessage("網絡異常,請重試")
.setPositiveButton("重試", (d, w) -> retry())
.setNegativeButton("退出", (d, w) -> finish())
.show();
}
DialogFragment 由 FragmentManager 管理,能自動處理配置變更、生命周期,避免 Window Leak。
public class NetErrorDialog extends DialogFragment {
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(requireActivity())
.setMessage("網絡異常")
.setPositiveButton("重試", (d, w) -> ((Retryable)requireActivity()).onRetry())
.create();
}
}
// 調用
new NetErrorDialog().show(getSupportFragmentManager(), "net_error");
// ViewModel + LiveData 模式,UI 組件自動感知生命周期
viewModel.getNetworkError().observe(this, error -> {
if (error != null) showDialog();
});
❌ `new AlertDialog.Builder(getApplicationContext())` — 無法添加 Window Token。
✅ `Toast.makeText(getApplicationContext(), ...)` — Toast 不依附 Window。
| 場景 | 推薦方案 |
|---|---|
| 簡單彈窗,邏輯輕量 | 方案一:`isFinishing()/isDestroyed()` 守衛 |
| 複雜交互、需保持狀態、配置變更 | 方案二:DialogFragment |
| MVVM 架構、解耦 UI 與邏輯 | 方案三:LiveData + ViewModel |