Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

android.view.WindowManager$BadTokenException: Unable to add window -- at Toast

Tags:

android

toast

When I do some action frequently(my assumption, is that due to Toast message) on my android app,getting below error.Im not getting exact location of this Issue.Can I get help from someone to resolve the same?

 --------- 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) 

Notes: This crash is not coming always.

like image 449
kavie Avatar asked Oct 04 '18 14:10

kavie


2 Answers

This is an issue of Toast officially fixed after Android 8.0, and can also be fixed by hooking WindowManagerWrapper.addView(view, params) with a third party lib PureWriter/ToastCompat.

Checking activity isFinishing cannot fix the crash due to Toast.show() is an asynchronous progress:

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

After Android 8.0, the crash is caught in handleShow (See the last few lines):

        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 */
                }
            }
        }
like image 132
thundertrick Avatar answered Nov 23 '22 03:11

thundertrick


Before passing a Context to the Toast, you should always check the validity of the context you are going to use. In my applications, I use a context-checker method I made:

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());
}

You can pass only a context, or also a Fragment if your current context is a fragment. This method checks if the context is an Activity, in this case we check if the activity is finishing/destroyed.

If you want to display the toast following fragment lifecycle, also pass to the method you current fragment, so we can tell whether the fragment is still visible and attached to the activity.

BONUS ANDROID 7.1

On API 25, this is not enough and sometimes the device still crash with the stacktrace you provided.

This repository might be the solution since it wraps the faulty call in a try/catch clause. Of course it's not the best thing to do, but at least solves this annoying crash for 7.1 devices.

like image 41
fillobotto Avatar answered Nov 23 '22 02:11

fillobotto