IllegalStateException представления всплывающих окон в предварительном просмотре Android P

Когда я пытался выпустить мое приложение в производство, отчет о предварительном запуске уведомил меня об ошибке на устройстве Pixel 2 Android P Preview. Ошибка связана с моим настраиваемым всплывающим сообщением, в котором говорится, что представление «уже добавлено в диспетчер окон»:

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 я вызываю этот метод, который увеличивает всплывающее окно 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));
}

Позже я установил для тоста динамически разные изображения в соответствии с выбором пользователя:

mToastCorrect.setView(mLayoutCorrect);

Я показываю тосты каждый раз, когда пользователь нажимает правильный / неправильный ответ, и отменяю другой тост, если он отображается:

    // 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. Если я раздуваю View только один раз во время MainActivity OnCreate, почему я получаю сообщение об ошибке, в котором говорится, что представление «уже добавлено в диспетчер окон»?

Удалось ли вам воспроизвести это на реальном устройстве или эмуляторе? Я подозреваю, что это проблема только в отчете перед запуском

Mark 26.09.2018 12:00

@Mark только на эмуляторе. У меня пока нет под рукой реального устройства с Android P.

Oshri 01.10.2018 20:07
5
2
2 161
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

После нескольких проб и ошибок мне удалось это исправить. Я надеюсь, что это поможет другим, столкнувшимся с той же проблемой.

Судя по всему, в Android P (API 28) изменилась обработка тостовых сообщений. В моем приложении всплывающие сообщения запускаются нажатием кнопки, поэтому всплывающее сообщение может быть вызвано до того, как было выполнено предыдущее всплывающее сообщение (обратите внимание, что два всплывающих сообщения вызываются из одного и того же объекта Toast). В версиях Android до P (API 28) нет проблем с запуском нового тоста до того, как будет выполнен предыдущий (даже если это тот же объект Toast) - новый тост просто переопределяет старый тост и запускается заново. Однако в Android P такое же поведение иногда может вызывать ошибку IllegalStateException.

Я уже сохранил ссылку на объект Toast для его повторного использования, поэтому мне просто пришлось отменить его, если он будет показан. Поскольку его отмена привела к нежелательному поведению API ниже 28 (например, всплывающее сообщение исчезает через очень короткое время), я вставил проверку версии. Это код обходного пути:

// 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 &&... вместо >=, а также для обоих тостов?

Mark 26.09.2018 10:35

@Mark У меня такой же код и для второго тоста, только не вставил его в ответ. Оно должно быть> = 28, так как я хочу отменить один и тот же тост только для Android P и выше. Для Android 27 и ниже нет необходимости отменять один и тот же тост, он отлично работает и без него. Более того, если я отменю его для Android 27 и ниже, он будет вести себя ненормально, поэтому я не хочу просто отменять его во всех случаях.

Oshri 01.10.2018 20:06

Команда try-catch не перехватывает исключение, потому что исключение выбрасывается из трассировки другого стека, которая принадлежит процедуре обработки другого сообщения пользовательского интерфейса. метод Toast.show () планирует только «событие», но «событие» происходит позже, а не в текущей трассировке стека.

Liu Tao 10.12.2018 10:44

Подумайте об использовании Build.VERSION_CODES.P вместо 28. Код будет более читабельным :)

LPVOID 14.08.2019 22:30

Я видел те же отчеты о сбоях на Pie, когда тот же пользовательский тост быстро запускался (запускается аппаратной кнопкой громкости). Отличие от варианта использования OP состоит в том, что у меня есть только один пользовательский экземпляр Toast, и обновляется только его пользовательский View.

И помимо этого сбоя (который я не мог воспроизвести в эмуляторе Pie, но видел в отчетах о сбоях), у меня есть еще одна проблема: при быстром вызове Toast.show () для того же тоста (скажем, 20 раз) только в лучшем случае первые 2 вызова отображают тост, а затем исчезают. Отмена тоста перед его показом не помогает.

Вывод, тосты действительно ломаются на П ...

Используете ли вы один и тот же объект Toast для отображения сообщений или используете новый (возможно, без ссылок) Toast каждый раз, когда вы его показываете? Я использую те же два объекта Toast, которые являются членами класса. Может, в этом разница между нами?

Oshri 24.08.2018 18:28

Я использую один экземпляр Toast, который создается один раз (например, вы, за исключением того, что у вас есть 2 экземпляра), использую настраиваемый View (например, вы), и я неоднократно вызываю show (), при этом только настраиваемый View обновляется между вызовами show (). Моя основная проблема, помимо сбоя этого сообщения, заключается в том, что несколько быстрых последовательных вызовов show () не работают: только этот первый вызов (и, в конечном итоге, второй) действительно показывает тост. тот же код отлично работает до Pie. Я собираюсь попробовать ваш метод, используя 2 тоста и отменяя предыдущий при показе следующего.

b0b 24.08.2018 18:53

После дополнительного тестирования Toast определенно не работает на Pie, если вы хотите отображать один и тот же Toast последовательно, быстро и визуально без проблем для пользователя. Например, показ того же тоста (экземпляра), который отображает счетчик от 0 до 100. Это возможно раньше в Oreo и раньше, но в Pie вам нужно отменить () ранее показанный тост (в противном случае новый просто не отображается ), что не позволяет отображать описанный сценарий (переход от 0 до 100) без визуальных сбоев. Вполне возможно, что Toast никогда не были предназначены для этого.

b0b 25.08.2018 01:00

Кроме того, существует проблема сбоя, описанная в OP, и ее можно легко воспроизвести, вызвав toast.cancel (); toast.show () несколько раз для одного тоста (даже в Oreo). Я, вероятно, отправлю отчет об ошибке в системе отслеживания проблем Android.

b0b 25.08.2018 01:01

Стоит включить ссылку на систему отслеживания проблем: Issuesetracker.google.com/issues/71731536

Mark 26.09.2018 12:05

@Mark спасибо за ссылку. Есть ли шанс, что это будет скоро исправлено командой разработчиков Android?

Oshri 01.10.2018 20:12

Другие вопросы по теме