【问题标题】:IllegalStateException of toast View on Android P previewAndroid P 预览版 toast View 的 IllegalStateException
【发布时间】:2019-01-28 03:09:20
【问题描述】:

在尝试发布我的应用以进行生产时,发布前的报告通知我 Pixel 2 Android P Preview 设备上出现错误。该错误与我拥有的自定义 toast 消息有关,说“已将视图添加到窗口管理器”:

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)

在 MainActivity OnCreate 中,我调用这个方法来膨胀 toast 视图:

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

我稍后会根据用户的选择动态地为 toast 设置不同的图像:

mToastCorrect.setView(mLayoutCorrect);

每次用户点击正确/错误答案时,我都会显示 toast 消息,如果显示,则取消另一个 toast:

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

感谢任何帮助!

  1. 我应该如何解决这个问题?
  2. 为什么我只在 Android P Preview 设备上收到此错误?
  3. 如果我在 MainActivity OnCreate 期间只为 View 充气一次,为什么会收到一条错误消息,提示该视图“已添加到窗口管理器”?

【问题讨论】:

  • 您是否设法在真实设备或模拟器上重现此内容?我怀疑这只是发布前报告中的一个问题
  • @Mark 只是在模拟器上。我手头还没有真正的 Android P 设备。

标签: android illegalstateexception android-inflate android-toast


【解决方案1】:

我在 Pie 上看到了相同的崩溃报告,快速触发相同的自定义 Toast(由硬件音量按钮触发)。与 OP 用例的不同之处在于,我只有一个自定义 Toast 实例,只有它的自定义 View 被更新。

除了这次崩溃(我无法在 Pie 模拟器中复制自己,但在崩溃报告中看到),我还有另一个问题:在同一个 toast 上快速调用 Toast.show() 时(比如 20 次),只有充其量前 2 个调用显示吐司然后消失。在显示之前取消 toast 并没有帮助。

结论,吐司在P上真的坏掉了……

【讨论】:

  • 您是使用同一个 Toast 对象来显示消息,还是每次显示时都使用一个新的(可能未引用的)Toast?我使用相同的两个 Toast 对象,它们是类成员。也许这就是我们之间的区别?
  • 我使用一个构建一次的 Toast 实例(像你一样,除了你有 2 个实例),使用自定义视图(像你一样),我重复调用 show() 只是自定义视图在调用 show() 之间更新。除了这篇文章的崩溃之外,我的主要问题是对 show() 的几个快速连续调用不起作用:只有第一次调用(最终是第二次调用)确实显示了 toast。相同的代码在 Pie 之前完美运行。我将尝试您的方法,使用 2 个 toast,并在显示下一个时取消上一个。
  • 经过更多测试,当您想连续显示相同的 Toast 时,Pie 上的 Toast 肯定会被破坏,为用户快速且视觉上无缝地更新。例如,显示显示计数器从 0 到 100 的相同 toast(实例)。这在 Oreo 之前和之前是可能的,但在 Pie 上,您必须 cancel() 先前显示的 Toast(否则根本不会显示新的 Toast ),这样就无法在没有视觉故障的情况下显示所描述的场景(从 0 到 100)。 Toast 很可能一开始就不是为此而设计的。
  • 然后是 OP 中描述的崩溃问题,可以通过调用 toast.cancel(); toast.show() 在同一个吐司上重复多次(甚至在奥利奥上)。我可能会在 Android 问题跟踪器上提交错误报告。
  • 值得包括问题跟踪链接:issuetracker.google.com/issues/71731536
【解决方案2】:

经过几次尝试和错误,我设法修复了它。我希望它能帮助遇到同样问题的其他人。

显然,在 Android P (API 28) 中,toast 消息处理发生了变化。在我的应用程序中,toast 消息由按钮单击触发,因此可以在前一个 toast 消息完成之前调用 toast 消息(请注意,两个 toast 消息是从同一个 Toast 对象调用的)。在 P (API 28) 之前的 Android 版本上,在前一个 toast 完成之前启动新的 toast 没有问题(即使它是相同的 Toast 对象) - 新的 toast 只是覆盖旧的 toast 并重新开始。 但是,在 Android P 中,同样的行为有时可能会抛出 IllegalStateException

我已经保留了对 Toast 对象的引用以重复使用它,所以我只需要取消它以防它被显示。由于取消它会导致 API 低于 28 的不良行为(例如,toast 消息在很短的时间后消失),因此我插入了版本检查。这是解决方法代码:

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

让我感到困惑的是为什么 try-catch 代码没有捕捉到异常(应用程序崩溃)。

【讨论】:

  • 难道你不应该用VERSION.SDK_INT < 28 &&... 而不是>= 以及两个toasts 吗?
  • @Mark 我也有第二次吐司的相同代码,只是没有把它放在回复中。它应该 >= 28,因为我只想为 Android P 及更高版本取消相同的 toast。对于 android 27 及以下版本,无需取消相同的 toast,没有它也可以正常工作。此外,如果我为 android 27 及以下版本取消它,它的行为会异常,所以我不想在所有情况下都取消它。
  • try-catch 没有捕获异常,因为该异常是从另一个堆栈跟踪中抛出的,该堆栈跟踪属于另一个 UI 消息的处理过程。 Toast.show() 方法只安排一个“事件”,但“事件”发生在以后,不再出现在当前堆栈跟踪中。
  • 考虑使用 Build.VERSION_CODES.P 而不是 28。代码将更具可读性:)
  • @LiuTao 是对的!尝试 catch 块不起作用.. 崩溃仍在发生
猜你喜欢
  • 1970-01-01
  • 2018-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-12
  • 2015-08-12
  • 2014-09-06
  • 1970-01-01
相关资源
最近更新 更多