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

单Activity含13个RecyclerView及SearchView仅在API25模拟器报BadTokenException的原因?

解决API 25 Nougat下Toast抛出WindowManager$BadTokenException问题

问题场景

  • 单个Activity中集成13个RecyclerView及对应的SearchView
  • 仅在4.65英寸720p(Galaxy Nexus)API 25 Nougat模拟器出现崩溃
  • 其他设备/模拟器(API 22/23/26、Pixel系列API 27)运行正常
  • 崩溃日志:

android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@a515134 is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:679)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:342)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
at android.widget.Toast$TN.handleShow(Toast.java:459)
at android.widget.Toast$TN$2.handleMessage(Toast.java:342)

原因分析

这个崩溃的核心是Toast尝试在Activity已销毁或处于不可用状态时显示。API 25的Toast实现逻辑和其他版本存在差异:

  • 早期Android版本中,Toast会自动处理Activity状态,即便Activity已销毁,最多只是不显示Toast,不会崩溃
  • 但API 25中,如果Activity的WindowToken已失效,Toast会直接抛出BadTokenException

另外,13个RecyclerView+SearchView的场景大概率存在大量异步操作(比如搜索结果回调、列表加载完成后弹Toast),当Activity发生生命周期变化(比如屏幕旋转、切后台),API 25下的WindowToken可能提前失效,而异步任务仍在执行,最终触发崩溃。

解决方案

1. 显示Toast前先校验Activity状态

在调用Toast显示方法前,先判断Activity是否处于活跃状态,避免在销毁后触发显示:

if (!isFinishing() && !isDestroyed()) {
    Toast.makeText(this, "你的提示内容", Toast.LENGTH_SHORT).show();
}

如果需要兼容API 17以下版本,可以用getWindow().getDecorView().isAttachedToWindow()替代isDestroyed()。

2. 统一管理Toast实例

频繁创建Toast可能在API 25下引发Token冲突,建议全局维护一个Toast实例,更新内容后再显示:

private Toast mToast;

public void showToast(String message) {
    if (mToast == null) {
        mToast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
    } else {
        mToast.setText(message);
    }
    if (!isFinishing() && !isDestroyed()) {
        mToast.show();
    }
}

3. 绑定异步任务与Activity生命周期

13个RecyclerView和SearchView带来的异步任务(搜索、列表加载),必须在Activity销毁时及时取消,避免后续回调触发Toast:

  • 用AsyncTask的话,在onDestroy()中调用cancel(true)
  • 用Coroutines的话,在onDestroy()中取消对应的Job
  • 用RxJava的话,在onDestroy()中调用dispose()解除订阅

4. 针对API 25捕获异常

直接捕获API 25下的BadTokenException,避免崩溃:

public void safeShowToast(Context context, String message) {
    try {
        Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
    } catch (WindowManager.BadTokenException e) {
        // 仅在API 25下可能触发,捕获后不做额外处理或记录日志
        e.printStackTrace();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 14:10:47