IllegalStateException of toast View on Android P preview

限于喜欢 提交于 2020-01-01 12:11:31

问题


While trying to release my app for production, the pre-launch report notified me of an error on Pixel 2 Android P Preview device. The error is related to a custom toast message I have, saying that a View "has already been added to the window manager":

java.lang.IllegalStateException: View android.support.constraint.ConstraintLayout{efbeb21 V.E...... ......ID 0,0-788,1124 #7f0900db app:id/toast_correct_container} has already been added to the window manager.
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:328)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
at android.widget.Toast$TN.handleShow(Toast.java:499)
at android.widget.Toast$TN$1.handleMessage(Toast.java:403)
at android.os.Handler.dispatchMessage(Handler.java:106)
at androidx.test.espresso.base.Interrogator.a(Interrogator.java:19)
at androidx.test.espresso.base.UiControllerImpl.a(UiControllerImpl.java:142)
at androidx.test.espresso.base.UiControllerImpl.a(UiControllerImpl.java:134)
at androidx.test.espresso.base.UiControllerImpl.a(UiControllerImpl.java:34)
at androidx.test.espresso.action.MotionEvents.a(MotionEvents.java:74)
at androidx.test.espresso.action.MotionEvents.a(MotionEvents.java:52)
at androidx.test.espresso.action.Tap.c(Tap.java:9)
at androidx.test.espresso.action.Tap.a(Tap.java:19)
at androidx.test.espresso.action.Tap$1.b(Tap.java:2)
at androidx.test.espresso.action.GeneralClickAction.perform(GeneralClickAction.java:22)
at androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform(ViewInteraction.java:9)
at androidx.test.espresso.ViewInteraction.a(ViewInteraction.java:78)
at androidx.test.espresso.ViewInteraction.a(ViewInteraction.java:94)
at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:3)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

In MainActivity OnCreate I call this method that inflates the toast View:

private void initToastObjects() {
    mToastCorrect = new Toast(this);
    mToastWrong = new Toast(this);

    // inflate view
    LayoutInflater myInflater = LayoutInflater.from(this);
    mLayoutCorrect = myInflater.inflate(R.layout.toast_correct, (ViewGroup) findViewById(R.id.toast_correct_container));
    mLayoutWrong = myInflater.inflate(R.layout.toast_wrong, (ViewGroup) findViewById(R.id.toast_wrong_container));
}

I later set dynamically different images to the toast according to user selection:

mToastCorrect.setView(mLayoutCorrect);

I show the toast messages every time the user click correct/wrong answers, and cancel the other toast if it's displayed:

    // cancel previous wrong answer toast and display correct answer toast
    try {
        if (mToastWrong.getView().isShown()) {
            mToastWrong.cancel();
        }
        mToastCorrect.show();
    } catch (Exception e) {
        e.printStackTrace();
    }

Any help is appreciated!

  1. How should I fix this issue?
  2. Why do I get this error only on Android P Preview device?
  3. If I inflate the View only once during MainActivity OnCreate, how come I get an error saying that the view "has already been added to the window manager"?

回答1:


After few trials and errors, I have managed to fix it. I hope it would help others that encounter the same issue.

Apparently, toast messages handling was changed in Android P (API 28). In my app, toast messages are triggered by button clicks, so a toast message can be invoked before the previous toast message was done (note that the two toast messages are invoked from the same Toast object). On Android versions prior to P (API 28), there is no issue with starting a new toast before the previous one is done (even if it's the same Toast object) - the new toast just overrides the old toast and starts again. However, in Android P the same behavior may sometimes throw IllegalStateException.

I've already kept reference to the Toast object to reuse it, so I just had to cancel it in case it was shown. Since cancelling it resulted with unwanted behavior for API lower than 28 (e.g. toast message disappearing after a very short time), I have inserted a version check. This is the workaround code:

// cancel previous toast and display correct answer toast
try {
    if (mToastWrong.getView().isShown()) {
        mToastWrong.cancel();
    }
    // cancel same toast only on Android P and above, to avoid IllegalStateException on addView
    if (Build.VERSION.SDK_INT >= 28 && mToastCorrect.getView().isShown()) {
        mToastCorrect.cancel();
    }
    mToastCorrect.show();
} catch (Exception e) {
    e.printStackTrace();
}

What still puzzles me is why the try-catch code didn't catch the exception (the app crashed).




回答2:


I have seen the same crash reports on Pie, firing the same custom Toast fast (triggered by hardware volume button). The difference to the OP's use case is that I have only one custom Toast instance with only its custom View being updated.

And besides this crash (that I could not reprod myself in the Pie emulator but seen in crash reports), I have another issue: when quickly calling Toast.show() on the same toast (say, 20 times), only at best the first 2 calls display the toast then it disappear. Cancelling the toast before showing it does not help.

Conclusion, toast are really broken on P...



来源:https://stackoverflow.com/questions/51956971/illegalstateexception-of-toast-view-on-android-p-preview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!