android.view.WindowManager$BadTokenException: Unable to add window"
Проблема:
Это исключение возникает, когда приложение пытается уведомить пользователя из фонового потока (AsyncTask), открыв диалоговое окно.
Если вы пытаетесь изменить пользовательский интерфейс из фонового потока (обычно из onPostExecute () AsyncTask), и если действие переходит к завершающему этапу, то есть прямому вызову finish (), пользователь нажимает кнопку «домой» или «назад» или активность
blockquote>Причина:
Причиной этого исключения является то, что, как говорится в сообщении об исключении, активность завершено, но вы пытаетесь отобразить диалог с контекстом готовой деятельности. Поскольку окно диалога для отображения среды андроида не отображается, вызывается это исключение.
blockquote>Решение:
Использовать метод
isFinishing()
, который вызывается Android чтобы проверить, находится ли эта деятельность в процессе отделки: будь то явный финиш () вызов или очистка активности, сделанная Android. Используя этот метод, очень легко избежать открытия диалога из фонового потока при завершении операции.Также поддерживать
blockquote>weak reference
для активности (а не сильную ссылку, чтобы активность можно было уничтожить, ), например.
private class chkSubscription extends AsyncTask<String, Void, String>{ private final WeakReference<login> loginActivityWeakRef; public chkSubscription (login loginActivity) { super(); this.loginActivityWeakRef= new WeakReference<login >(loginActivity) } protected String doInBackground(String... params) { //web service call } protected void onPostExecute(String result) { if(page.contains("error")) //when not subscribed { if (loginActivityWeakRef.get() != null && !loginActivityWeakRef.get().isFinishing()) { AlertDialog.Builder builder = new AlertDialog.Builder(login.this); builder.setCancelable(true); builder.setMessage(sucObject); builder.setInverseBackgroundForced(true); builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton){ dialog.dismiss(); } }); builder.show(); } } } }
Обновление: если вы не выполняете какой-либо пользовательский интерфейс, используйте эту ссылку (например,
Токены окна:
Как следует из его названия, токен окна является специальным типом маркера Binder, который оконный менеджер использует для уникальной идентификации окна в системе. Ярлыки окна важны для безопасности, потому что они делают невозможным использование вредоносных приложений поверх окон других приложений. Менеджер окон защищает от этого, требуя от приложений передать токен своего приложения как часть каждого запроса на добавление или удаление окна. Если жетоны не совпадают, диспетчер окон отклоняет запрос и генерирует исключение BadTokenException. Без токенов окна этот необходимый шаг идентификации не был бы возможен, и оконный менеджер не смог бы защитить себя от вредоносных приложений.
blockquote>Реальный сценарий:
Когда приложение запускается в первый раз, ActivityManagerService создает специальный вид токена окна, называемый токеном окна приложения, который однозначно идентифицирует окно контейнера верхнего уровня приложения. Диспетчер активности предоставляет этот токен как для приложения, так и для диспетчера окон, и приложение отправляет токен в оконный менеджер каждый раз, когда хочет добавить на экран новое окно. Это обеспечивает безопасное взаимодействие между приложением и диспетчером окон (делая невозможным добавление окон поверх других приложений), а также упрощает диспетчеру активности непосредственные запросы к диспетчеру окон.
BLOCKQUOTE>
Примечание: это была ошибка в обработке CPython для
< / blockquote>yield
в выражениях выражений и выражений генератора, исправленных в Python 3.8, с предупреждением об отказе в Python 3.7. Смотрите отчет об ошибках Python и записи What's New для Python 3.7 и Python 3.8 .Выражения генератора, а также функции set и dict компилируются в объекты функции (генератора). В Python 3 переписные справки получают одинаковое обращение; все они, по существу, представляют собой новую вложенную область.
Вы можете увидеть это, если попытаетесь разобрать выражение генератора:
>>> dis.dis(compile("(i for i in range(3))", '', 'exec')) 1 0 LOAD_CONST 0 (<code object <genexpr> at 0x10f7530c0, file "", line 1>) 3 LOAD_CONST 1 ('<genexpr>') 6 MAKE_FUNCTION 0 9 LOAD_NAME 0 (range) 12 LOAD_CONST 2 (3) 15 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 18 GET_ITER 19 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 22 POP_TOP 23 LOAD_CONST 3 (None) 26 RETURN_VALUE >>> dis.dis(compile("(i for i in range(3))", '', 'exec').co_consts[0]) 1 0 LOAD_FAST 0 (.0) >> 3 FOR_ITER 11 (to 17) 6 STORE_FAST 1 (i) 9 LOAD_FAST 1 (i) 12 YIELD_VALUE 13 POP_TOP 14 JUMP_ABSOLUTE 3 >> 17 LOAD_CONST 0 (None) 20 RETURN_VALUE
Вышеприведенное показывает, что выражение генератора скомпилированный в объект кода, загруженный как функция (
MAKE_FUNCTION
создает объект функции из объекта кода). Ссылка.co_consts[0]
позволяет нам видеть объект кода, сгенерированный для выражения, и используетYIELD_VALUE
так же, как функция генератора.Таким образом, выражение
yield
работает в этом контексте, поскольку компилятор рассматривает их как скрытые функции.Это ошибка;
yield
не имеет места в этих выражениях. Python грамматика до Python 3.7 позволяет (поэтому код компилируется), но спецификация выраженияyield
показывает, что использованиеyield
здесь не должно действительно работать :Выражение yield используется только при определении функции generator и, следовательно, может использоваться только в теле определения функции.
blockquote >Это было подтверждено как ошибка в выпуске 10544 . Разрешение ошибки заключается в том, что с помощью
yield
иyield from
будет поднятьSyntaxError
в Python 3.8 ; в Python 3.7 он вызываетDeprecationWarning
, чтобы гарантировать, что код перестает использовать эту конструкцию. Вы увидите то же предупреждение в Python 2.7.15 и выше, если вы используете переключатель командной строки-3
, включающий предупреждения о совместимости с Python 3.Появляется предупреждение 3.7.0b1 как это; включение предупреждений в ошибки дает вам исключение
SyntaxError
, как и в 3.8:>>> [(yield i) for i in range(3)] <stdin>:1: DeprecationWarning: 'yield' inside list comprehension <generator object <listcomp> at 0x1092ec7c8> >>> import warnings >>> warnings.simplefilter('error') >>> [(yield i) for i in range(3)] File "<stdin>", line 1 SyntaxError: 'yield' inside list comprehension
. Различия между тем, как
yield
в понимании списка иyield
в выражении генератора работают, вытекают из различия в том, как эти два выражения реализованы. В Python 3 в понимании списка используются вызовыLIST_APPEND
, чтобы добавить верхнюю часть стека в построенный список, в то время как выражение генератора вместо этого дает это значение. Добавление в(yield <expr>)
просто добавляет еще один код операцииYIELD_VALUE
:>>> dis.dis(compile("[(yield i) for i in range(3)]", '', 'exec').co_consts[0]) 1 0 BUILD_LIST 0 3 LOAD_FAST 0 (.0) >> 6 FOR_ITER 13 (to 22) 9 STORE_FAST 1 (i) 12 LOAD_FAST 1 (i) 15 YIELD_VALUE 16 LIST_APPEND 2 19 JUMP_ABSOLUTE 6 >> 22 RETURN_VALUE >>> dis.dis(compile("((yield i) for i in range(3))", '', 'exec').co_consts[0]) 1 0 LOAD_FAST 0 (.0) >> 3 FOR_ITER 12 (to 18) 6 STORE_FAST 1 (i) 9 LOAD_FAST 1 (i) 12 YIELD_VALUE 13 YIELD_VALUE 14 POP_TOP 15 JUMP_ABSOLUTE 3 >> 18 LOAD_CONST 0 (None) 21 RETURN_VALUE
Код операции
YIELD_VALUE
в индексах байт-кода 15 и 12 соответственно является дополнительным, кукушкой в гнезде. Таким образом, для генератора, использующего список, вы получаете 1 выход, каждый раз производя верхнюю часть стека (заменяя верхнюю часть стека на возвращаемое значениеyield
), а для варианта выражения генератора вы получаете верхнюю часть stack (целое число), а затем снова , но теперь стек содержит возвращаемое значениеyield
, и вы получитеNone
второй раз.Для списка тогда предполагается, что предполагаемый вывод объекта
list
все еще возвращен, но Python 3 рассматривает это как генератор, поэтому вместо этого значения возвратаStopIteration
в качестве атрибутаvalue
добавляется возвращаемое значение:>>> from itertools import islice >>> listgen = [(yield i) for i in range(3)] >>> list(islice(listgen, 3)) # avoid exhausting the generator [0, 1, 2] >>> try: ... next(listgen) ... except StopIteration as si: ... print(si.value) ... [None, None, None]
Те
None
объекты - это возвращаемые значения из выраженийyield
.И повторить это снова; эта же проблема относится к словарю и устанавливает понимание в Python 2 и Python 3; в Python 2 возвращаемые значения
yield
все еще добавляются к предполагаемому словарю или заданному объекту, а возвращаемое значение «дано» последнему вместо привязки к исключениюStopIteration
:>>> list({(yield k): (yield v) for k, v in {'foo': 'bar', 'spam': 'eggs'}.items()}) ['bar', 'foo', 'eggs', 'spam', {None: None}] >>> list({(yield i) for i in range(3)}) [0, 1, 2, set([None])]