android.view.WindowManager $ BadTokenException:无法添加窗口-在Toast

kav*_*vie 7 android toast

当我在Android应用上频繁执行某项操作(我的假设,是由于Toast消息所致)时,出现以下错误。我没有确切找到此问题的位置。我可以从某人那里寻求帮助来解决该问题吗?

 --------- beginning of crash
10-04 16:13:49.250 6541-6541/com.test.myapp E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.test.myapp, PID: 6541
    android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@e2815e 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)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6119)
        at java.lang.reflect.Method.invoke(Native Method) 
Run Code Online (Sandbox Code Playgroud)

注意:此崩溃并非总是会出现。

fil*_*tto 6

在将ContextTo 传递给Toast 之前,您应该始终检查要使用的上下文的有效性。在我的应用程序中,我使用了一个上下文检查器方法:

public static boolean isContextValid(Context context, Fragment fragment) {
    if (context instanceof Activity) {
        Activity activity = (Activity) context;
        if (activity.isFinishing() || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed())) {
            return false;
        }
    }

    return context != null && (fragment == null || (fragment.isAdded() && !fragment.isRemoving());
}
Run Code Online (Sandbox Code Playgroud)

您只能传递一个上下文,Fragment如果当前上下文是一个片段,也可以传递。此方法检查上下文是否为Activity,在这种情况下,我们检查活动是否已完成/销毁。

如果要在片段生命周期之后显示吐司,还可以传递到当前片段的方法,这样我们就可以判断该片段是否仍然可见并附加到活动中。

红利7.1

在API 25上,这还不够,有时设备仍会因您提供的stacktrace而崩溃。

该存储库可能是解决方案,因为它将错误的调用包装在try / catch子句中。当然,这并不是最好的做法,但至少可以解决7.1设备的这种令人讨厌的崩溃。


thu*_*ick 5

这是 Android 8.0 之后官方修复的问题Toast,也可以通过 hookWindowManagerWrapper.addView(view, params)第三方 lib PureWriter/ToastCompat来修复。

检查活动isFinishing无法修复崩溃,因为Toast.show()是异步进度:

Toast.makeText().show()
-> Toast.getService().enqueueToast()
-> Toast.TN.handleShow() // crash here, and unable to be caught from outside
Run Code Online (Sandbox Code Playgroud)

Android 8.0之后,崩溃发生在handleShow(见最后几行):

        public void handleShow(IBinder windowToken) {
            if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
                    + " mNextView=" + mNextView);
            // If a cancel/hide is pending - no need to show - at this point
            // the window token is already invalid and no need to do any work.
            if (mHandler.hasMessages(CANCEL) || mHandler.hasMessages(HIDE)) {
                return;
            }
            if (mView != mNextView) {
                // remove the old view if necessary
                handleHide();
                mView = mNextView;
                Context context = mView.getContext().getApplicationContext();
                String packageName = mView.getContext().getOpPackageName();
                if (context == null) {
                    context = mView.getContext();
                }
                mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
                // We can resolve the Gravity here by using the Locale for getting
                // the layout direction
                final Configuration config = mView.getContext().getResources().getConfiguration();
                final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
                mParams.gravity = gravity;
                if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
                    mParams.horizontalWeight = 1.0f;
                }
                if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
                    mParams.verticalWeight = 1.0f;
                }
                mParams.x = mX;
                mParams.y = mY;
                mParams.verticalMargin = mVerticalMargin;
                mParams.horizontalMargin = mHorizontalMargin;
                mParams.packageName = packageName;
                mParams.hideTimeoutMilliseconds = mDuration ==
                    Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
                mParams.token = windowToken;
                if (mView.getParent() != null) {
                    if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
                    mWM.removeView(mView);
                }
                if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
                // Since the notification manager service cancels the token right
                // after it notifies us to cancel the toast there is an inherent
                // race and we may attempt to add a window after the token has been
                // invalidated. Let us hedge against that.
                try {
                    mWM.addView(mView, mParams);
                    trySendAccessibilityEvent();
                } catch (WindowManager.BadTokenException e) {
                    /* ignore */
                }
            }
        }
Run Code Online (Sandbox Code Playgroud)


Onu*_* D. 3

android.view.WindowManager$BadTokenException:无法添加窗口 - 令牌 android.os.BinderProxy@e2815e 无效;您的活动正在运行吗?

在这一行中,它向您提示,当您尝试显示祝酒词时,您的活动可能尚未运行。

如果您在这样的活动中展示 Toast,

Toast toast = Toast.makeText(this, R.string.message, Toast.LENGTH_LONG).show();
Run Code Online (Sandbox Code Playgroud)

您应该意识到,您正在尝试通过将第一个参数设置为“this”来在 Activity 的上下文中显示它,通过这种方式,如果您 finish(); 您在该行之前的活动,您会得到该异常。所以我建议检查是否是这种情况,或者您可以使用应用程序的上下文来代替:

Toast toast = Toast.makeText(getApplicationContext(), R.string.message, Toast.LENGTH_LONG).show();
Run Code Online (Sandbox Code Playgroud)

希望这有帮助!