Android view windowmanager badtokenexception unable to add window token

Android BadTokenException: Unable to add window, is your activity running

I faced this exception in one of my application and after a long research I found a solution for this problem.
This exception occurs when the app is trying to notify the user from the background thread (AsyncTask) by opening a Dialog.

If you are trying to modify the UI from background thread (usually from onPostExecute() of AsyncTask) and if the activity enters finishing stage i.e.) explicitly calling finish(), user pressing home or back button or activity clean up made by Android then you get this error.

android.view.WindowManager$BadTokenException: Unable to add window — token [email protected] is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:585)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:326)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:224)
android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:149)
at android.view.Window$LocalWindowManager.addView(Window.java:547)
at android.app.Dialog.show(Dialog.java:277)
com.ibc.activityasyncdemo.MainActivity$LongTask.onPostExecute(MainActivity.java:72)
com.ibc.activityasyncdemo.MainActivity$LongTask.onPostExecute(MainActivity.java:1)
at android.os.AsyncTask.finish(AsyncTask.java:631)
at android.os.AsyncTask.access$600(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)

Let us first create this scenario which causes the exception to be thrown, by using the following code. Here I want to show an AlertDialog in onPostExecute method of my AsyncTask. I pass my activity’s reference to the task’s constructor using “this”, storing it in an instance and using it to create a dialog.

If the activity finishes (either by calling finish() or if user presses back/home button) before the task completes, alertDialog.show() throws android.view.WindowManager$BadTokenException: Unable to add window — token [email protected] is not valid; is your activity running?.

The reason for this exception is that, as the exception message says, the activity has finished but you are trying to display a dialog with a context of the finished activity. Since there is no window for the dialog to display the android runtime throws this exception.

To solve this problem, we can use isFinishing() method which is called by Android to check whether this activity is in the process of finishing: be it explicit finish() call or activity clean up made by Android. By using this method it is very easy to avoid opening dialog from background thread when activity is finishing.

Also maintain a weak reference for the activity (and not a strong reference so that activity can be destroyed once not needed) and check if the activity is not finishing before performing any UI using this activity reference (i.e. showing a dialog).

Weak references are useful for mappings that should have their entries removed automatically once they are not referenced any more (from outside).

To check this, uncomment the finish() statement in onCreate() and run this program. The dialog will not pop up because there is no reference to the activity since it has been finished.

Источник

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:

Читайте также:  All samsung galaxy android phones

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

Недавно наша команда завершила разработку приложения под 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 потока, так как это может занимать некоторое время и при этом основной поток блокируется и не отвечает на другие события. Мы старались избегать подобного поведения, но в одном из мест все-таки остался простой сетевой вызов. Решена эта проблема была тем, что мы вынесли этот код в другой поток.

Читайте также:  Colibrism mobile app android ios
Поворот устройства — что может быть проще и привычнее для пользователя?

Казалось бы, для мобильных устройств смена ориентации экрана — это вещь настолько часто используемая и привычная, что это должно отразиться и в 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.

Давайте рассмотрим загрузку данных в другом потоке чуть более подробно, так как и при этом нас ждали неожиданные “сюрпризы”.

Читайте также:  Андроид не подключается автоматически bluetooth

Немного теории: для облегчения создания и работы в потоке отличном от основного 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 . Даже там, где это совсем неочевидно.

Источник

Оцените статью