- android.view.WindowManager$BadTokenException: Unable to add window — token null is not valid; is your activity running? #92
- Comments
- epool commented Aug 14, 2020
- skydoves commented Aug 15, 2020 •
- epool commented Aug 17, 2020 •
- skydoves commented Oct 7, 2020
- znakeeye commented Oct 16, 2020
- sembozdemir commented Oct 19, 2020
- skydoves commented Nov 7, 2020
- znakeeye commented Nov 10, 2020
- Android 1.6: «android.view.WindowManager $BadTokenException: Невозможно добавить окно — токен null не для приложения»
- ОТВЕТЫ
- Ответ 1
- Ответ 2
- Ответ 3
- Ответ 4
- Ответ 5
- Ответ 6
- Ответ 7
- Ответ 8
- Ответ 9
- Ответ 10
- Ответ 11
- Ответ 12
- Ответ 13
- Ответ 14
- Ответ 15
- Ответ 16
- Android 1.6: «android.вид.WindowManager$BadTokenException: невозможно добавить окно-токен null не для приложения»
- 16 ответов
- Некоторые “подводные камни” разработки под Android
android.view.WindowManager$BadTokenException: Unable to add window — token null is not valid; is your activity running? #92
Comments
epool commented Aug 14, 2020
Please complete the following information:
- Library Version [v1.1.9]
- Affected Device(s) [General]
Describe the Bug:
We are receiving this crash in our crashlytics console
This is a dummy implementation of our view for a reference implementation:
Expected Behavior:
Don’t receive these crashes.
The text was updated successfully, but these errors were encountered:
skydoves commented Aug 15, 2020 •
Hi, @epool !
Could you test using this way?
I think your Activity is destroyed but still seems to show and reference the destroyed Activity’s context.
Or the custom view is used in a fragment?
epool commented Aug 17, 2020 •
@skydoves yes, it’s being used in an Activity. The reason why I’m not using myLifeCycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY) is because the custom view is being added and removed from its parent programmatically and when it’s added again after being removed I want to still listening the events, otherwise after the Lifecycle.Event.ON_DESTROY the custom view stops receiving the view model events. I’ll try to use the activity as lifecycleOwner = . The weird thing is that the custom view is declared inside of the activity and has the same life cycle scope in theory.
skydoves commented Oct 7, 2020
Hi, @epool!
Could you let me know about this issue going on?
znakeeye commented Oct 16, 2020
This could happen if you show a tooltip in some event handler where the event is handled just after e.g. user has pressed the back button.
I tried safe-guarding by checking fragment.isAdded() and possibly also (don’t remember) fragment.isRemoving() but it still crashed. My fix was to skip the tooltip entirely.
For sure, we need some extra checks when showing the balloon. Will try to isolate this in a sample project at some point. Did the OP solve this?
sembozdemir commented Oct 19, 2020
This issue is also happening for my app. Stacktrace is the same. I use it in an activity. I also use LiveData.
skydoves commented Nov 7, 2020
@znakeeye @sembozdemir
I released a new version 1.2.5.
This update includes preventing to show popups in activities when the window is already detached from the activities.
Please let me know if the same crash continues. Thanks!
znakeeye commented Nov 10, 2020
@skydoves @sembozdemir
So far, I have not been able to reproduce this error in my setup. Very encouraging! Would be great if someone could confirm the fix in a published app.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Источник
Android 1.6: «android.view.WindowManager $BadTokenException: Невозможно добавить окно — токен null не для приложения»
Я пытаюсь открыть диалоговое окно, но каждый раз, когда я пытаюсь его открыть, он генерирует это исключение:
Я создаю его, вызывая showDialog с идентификатором отображения. Обработчик обработчика onCreateDialog отлично работает, и я могу пройти через него без проблем, но я привязал его, поскольку кажется, что я чего-то не хватает:
Есть ли что-то от этого? Некоторые вопросы говорили об этой проблеме при создании диалога из onCreate , который происходит потому, что активность еще не создана, но это происходит из вызова из объекта меню, а переменная appContext кажется, что она правильно заполнен в отладчике.
ОТВЕТЫ
Ответ 1
то есть. эта строка: Context appContext = this.getApplicationContext(); должен идти, а вместо этого вы используете указатель на активность, в которой вы находитесь (возможно, это).
Я тоже укусил это сегодня, раздражающая часть getApplicationContext() является дословным от developer.android.com: (
Ответ 2
Вы не можете отобразить окно/диалог приложения через контекст, который не является активностью. Попробуйте передать действительную ссылку на действия
Ответ 3
То же самое можно сказать о функции getApplicationContext.
Документы на сайте android говорят использовать его, но он не работает. grrrrr:-P
«this» обычно является вашей активностью, с которой вы начинаете диалог.
Ответ 4
В документах Android предлагается использовать getApplicationContext();
но он не будет работать вместо того, чтобы использовать текущую активность при создании AlertDialog.Builder или AlertDialog или Dialog.
Ответ 5
Вместо getApplicationContext() просто используйте ActivityName.this
Ответ 6
У меня была аналогичная проблема, когда у меня был другой класс:
Работала отлично в большинстве случаев, но иногда она разбивалась с той же ошибкой. Тогда я понимаю, что в MyActivity у меня было.
Поскольку я удерживал объект как static , второй запуск кода по-прежнему содержал исходную версию объекта и, следовательно, все еще имел в виду исходный Activity , который долго не существовал.
Глупая глупая ошибка, тем более, что мне действительно не нужно было держать объект как static в первую очередь.
Ответ 7
Просто измените его на
Ответ 8
Другим решением является установка типа окна в системный диалог:
Для этого требуется разрешение SYSTEM_ALERT_WINDOW :
Как говорится в документах:
Очень немногие приложения должны использовать это разрешение; эти окна предназначены для взаимодействия на уровне системы с пользователем.
Это решение, которое вы должны использовать, только если вам требуется диалог, не связанный с активностью.
Ответ 9
Не используйте getApplicationContext() при объявлении dialouge
Всегда используйте this или ваш activity.this
Ответ 10
Это работало для меня —
Ответ 11
Для вложенных диалогов эта проблема очень распространена, она работает, когда
Ответ 12
Вы также можете сделать это
Это сработало для меня!!
Ответ 13
Текст здесь: —
используйте это для получения контекста activity для progressdialog
или progressdialog = new ProgressDialog(this);
используйте это для получения контекста приложения для BroadcastListener не для progressdialog .
Ответ 14
Как говорится, для контекста требуется контекст для действия, используйте «YourActivity.this» для статического контекста или проверьте здесь, как использовать динамический один в безопасном режиме
Ответ 15
Попробуйте reset dialog тип окна
Не забудьте использовать разрешение android.permission.SYSTEM_ALERT_WINDOW
Ответ 16
Самый лучший и самый безопасный способ показать «ProgressDialog» в AsyncTask, избегая проблемы с утечкой памяти, — это использовать «Обработчик» с Looper.main().
Теперь вы выполнили настройку. Теперь вызовите showProgress() и hideProgress() в AsyncTask.
Источник
Android 1.6: «android.вид.WindowManager$BadTokenException: невозможно добавить окно-токен null не для приложения»
Я пытаюсь открыть диалоговое окно, но каждый раз, когда я пытаюсь открыть его, он бросает это исключение:
Я создаю его, позвонив showDialog С кодом на дисплее. The onCreateDialog обработчик регистрируется нормально, и я могу пройти через него без проблем, но я прикрепил его, так как кажется, что мне чего-то не хватает:
чего-то не хватает? Некоторые вопросы говорили о наличии этой проблемы при создании диалога из onCreate , которым происходит потому, что действие еще не создано, но это происходит от вызова из объекта меню и appContext переменная кажется, что она правильно заполнена в отладчике.
16 ответов
вместо : Context appContext = this.getApplicationContext(); вы должны использовать указатель на действие, в котором вы находитесь (вероятно, this ).
меня сегодня тоже укусили, раздражающая часть-это getApplicationContext() дословно от developer.android.com : (
вы не можете отобразить окно/диалог приложения через контекст, который не является действием. Попробуйте передать допустимую ссылку на действие
то же самое с getApplicationContext.
документы на сайте android говорят использовать его, но он не работает. грррр : — П
«этот» обычно это ваша деятельность, с которой вы начинаете диалог.
документы Android предлагают использовать getApplicationContext ();
но это не будет работать вместо того, чтобы использовать вашу текущую деятельность при создании экземпляра AlertDialog.Строитель или AlertDialog или диалог.
Источник
Некоторые “подводные камни” разработки под Android
Недавно наша команда завершила разработку приложения под Android. В процессе разработки и затем поддержки мы столкнулись с некоторыми проблемами технического характера. Часть из них — это наши баги, которых мы могли бы избежать, другая часть — это совсем неочевидные особенности Android, которые либо плохо описаны в документации, либо не описаны вообще.
В этой статье я бы хотел рассмотреть несколько реальных багов, которые возникли у наших пользователей и рассказать о путях их решения.
Статья не претендует на подробный анализ потенциальных проблем, это просто рассказ из жизни одно реального Android приложения.
RTFM (http://en.wikipedia.org/wiki/RTFM)
Так как при разработке под Android надо иметь в виду, что ваше приложение может работать на огромном количестве различных устройств, то надо думать о проблемах их совместимости.
Вот например одна из ошибок, которая возникала у наших пользователей:
А причина — проста, согласно документации класс android.util.Patterns доступен начиная с версии API 8 (Android 2.2.x), а у пользователя была версия 2.1. Решили мы это конечно оберткой этого кода в try/catch .
Вот еще одна подобная проблема вызванная невнимательным чтением документации:
Все дело в том, что strict mode (http://developer.android.com/reference/android/os/StrictMode.html) был включен по умолчанию в Android начиная с версии 3.0. Это значит, что ваше приложение не может обращаться к сети напрямую из основного UI потока, так как это может занимать некоторое время и при этом основной поток блокируется и не отвечает на другие события. Мы старались избегать подобного поведения, но в одном из мест все-таки остался простой сетевой вызов. Решена эта проблема была тем, что мы вынесли этот код в другой поток.
Поворот устройства — что может быть проще и привычнее для пользователя?
Казалось бы, для мобильных устройств смена ориентации экрана — это вещь настолько часто используемая и привычная, что это должно отразиться и в API. Т.е. эта ситуация должна обрабатываться очень просто. Но нет. Тут много нюансов.
Допустим, что нам необходимо сделать приложение, которое загружает список чего-либо и выводит его на экран. Т.е. при старте Activity (в методе onCreate() ) мы запускаем поток (чтобы не блокировать UI поток), который будет загружать данные. Этот поток отрабатывает некоторое время, поэтому мы будет отображать процесс загрузки в ProgressDialog . Все просто и замечательно работает.
Но, после загрузки, пользователь повернул устройство и тут мы обнаруживаем, что снова появился ProgressDialog и мы опять загружаем наши данные. Но ведь ничего не изменилось? Просто человеку удобнее смотреть на этот список повернув устройство.
А все дело в том, что метод onCreate() вызывается не только при создании Activity , но и при повороте экрана! Но мы не хотим засталять пользователя снова ждать загрузки. Все что нам надо — это снова показать уже загруженные данные.
Если мы поищем в интернете, мы найдем много ссылок на описание этой проблемы и также огромное количество примеров как решить эту проблему. И что самое плохое, что большинство таких ссылок предлагает неверное решение. Вот пример этого — http://codesex.org/articles/33-android-rotate
Не делайте обработку поворота экрана через onConfigurationChanged() ! Это неверно! Официальная документация ясно утверждает что “Using this attribute should be avoided and used only as a last-resort.“ http://developer.android.com/guide/topics/manifest/activity-element.html#config
А вот правильный подход описан здесь — http://developer.android.com/guide/topics/resources/runtime-changes.html И как показывает практика — он не прост в имплементации.
Идея в том, что перед разворотом экрана Android вызовет метод onRetainNonConfigurationInstance() вашего Activity . И вы можете вернуть данные (например список загруженных объектов) из этого метода, которые и будут сохранены между 2 вызовами метода onCreate() вашего Activity . Затем при вызове onCreate() вы можете вызвать getLastNonConfigurationInstance() который и вернет вам сохраненные данные. Т.е. при создании Activity вы вызываете getLastNonConfigurationInstance() и если он вам вернул данные — то эти данные уже были загружены и надо только отобразить их. Если же не вернул данные — то запускаете загрузку.
Но вот на практике ситуация выглядит так. У нас может быть 2 варианта когда пользователь поворачивает устройство. Первый вариант — когда идет загрузка данных (работает наш поток который загружает данные и при этом отображается ProgressDialog ) или данные уже загружены и мы сохранили их в списке для отображения. Получается, что в первом случае мы при повороте должны сохранить ссылку на работающий поток, а во втором случае ссылку на уже загруженный список. Мы так и делаем.
Но это усложняет наш код и не кажется мне простым и интуитивным. Более того, когда идет смена ориентации экрана и мы сохранили поток загрузки данных, мы потом вынуждены при onCreate() опять восстанавливать ProgressDialog ! А если добавить сюда, что в нашем приложении пользователь может загружать данные из разных мест и у нас не один поток загрузки данных, а несколько — то количество кода которое обслуживает простой поворот экрана становится просто огромным.
Честно сказать, я не понимаю почему это было сделано так сложно.
Чуть более подробнее о потоках или использование AsyncTask.
Давайте рассмотрим загрузку данных в другом потоке чуть более подробно, так как и при этом нас ждали неожиданные “сюрпризы”.
Немного теории: для облегчения создания и работы в потоке отличном от основного UI потока был сделан специальный класс — AsyncTask (http://developer.android.com/reference/android/os/AsyncTask.html)
Суть его в том, что в нем уже есть готовые методы onPreExecute() и onPostExecute(Result) , которые выполняются в основном UI потоке и которые служат для отображение чего-либо и есть метод doInBackground(Params. ) внутри которого и происходит основная работа и он запускается в отдельном потоке автоматически. Вот примерный код как это выглядит:
Все просто и красиво.
Но теперь — немного практики. Вот Вам ошибка от реального пользователя:
А эта ошибка означает, что когда мы вызываем spinner.show() в методе onPreExecute() , то этот ProgressDialog созданный со ссылкой на MyActivity уже неактивен и его нет на экране! Ладно, я еще понимаю как такое может быть при вызове onPostExecute() . Т.е. например пока мы загружали данные пользователь нажал Back и наш ProgressDialog ушел с экрана. Но как такое может быть сразу при вызове загрузки, когда этот код стартует сразу при затуске Activity — это для меня неясно.
Но в любом случае, мы должны обрабатывать такие ситуации поэтому мы решили это обеткой методов spinner.show() и spinner.dismiss() в try/catch . Решение конечно не очень красивое, но в нашем случае — вполне функциональное.
Кстати, такой же код есть и например в Facebook SDK for Android, который был разработан другими опытными разрабочиками. И там тоже у нас были ситуации когда приложение вылетало при закрытии ProgressDialog . Нам пришлось добавить обработку и в их код. Так что проблема эта — не только в нас.
Добавьте сюда еще проблему, что была описана ранее. Что при повороте устройства надо пересоздавать ProgressDialog если поворот был во время загрузки данных. Это тоже добавит сюда вспомогательного кода.
И еще стоит вспомнить что метод doInBackground() выполняется в отдельном потоке и поэтому если при загрузке данных произошла ошибка, то нельзя выдать Alert прямо оттуда, так как это не UI поток. Надо сохранить ошибку, а затем после выхода из потока загрузки в методе onPostExecute(Void result) уже можно что-то показать.
Т.е. опять много вспомогательного кода и не все так просто…
AlarmManager
А еще есть моменты которые вообще не описаны в документации. Например, мы в своем приложении используем AlarmManager (http://developer.android.com/reference/android/app/AlarmManager.html) который помогает нам выдавать сообщения пользователю через некоторое время когда само наше приложение уже закрыто.
Этот AlarmManager — штука очень полезная, вот только проблема в том, что иногда он “теряет” созданные в нем нотификации! Мы потратили кучу времени чтобы понять как и почему это происходит, перерыли всю документацию и ничего не нашли. Совершенно случайно мы “набрели” на это обсуждение — http://stackoverflow.com/questions/9101818/how-to-create-a-persistent-alarmmanager.
Оказывается, что если приложение падает, или пользователь сам убивает приложение через task manager (что возможно и совершенно обычно), то ВСЕ нотификации для этого приложения в AlarmManager УДАЛЯЮТСЯ! Вот это — сюрприз! Особенно, мне понравился там один из комментариев: “Re Alarm canceled: Thanks for the clarification. I asked this on the Android team office hours g+ hangout and they even seemed confused about this behavior. Is it documented anywhere?”
Так что теперь при старте приложения мы вынуждены пересоздавать настроенные нотификации, так как даже нет API в AlarmManager чтобы проверить есть ли такие нотификации или нет.
Бывает и такое.
Вот вам еще несколько ошибок которые мы зарегистрировали у наших пользователей. В своем приложении мы используем OAuth идентификацию для различных социальных сетей и поэтому вынуждены запускать штатный браузер в Android (через который и должен работать OAuth). При этом, он периодически “падает”
Заключение
Несмотря на все вешеописанное, мне понравилась заниматься разработкой под Android. В основном API вполне продуманный и удобный.
Вот только везде стоит использовать try/catch . Даже там, где это совсем неочевидно.
Источник