Вызывает диалоговое окно «Невозможно добавить окно - нулевой токен не для приложения» с getApplication () в качестве контекста - PullRequest
639 голосов
/ 27 апреля 2011

My Activity пытается создать AlertDialog, для которого в качестве параметра требуется Context. Это работает, как и ожидалось, если я использую:

AlertDialog.Builder builder = new AlertDialog.Builder(this);

Однако я опасаюсь использовать «это» в качестве контекста из-за потенциальной утечки памяти, когда активность разрушается и воссоздается даже во время чего-то простого, например, при повороте экрана. Из связанной записи в блоге разработчика Android :

Существует два простых способа избежать утечек памяти, связанных с контекстом. Самый очевидный из них - избежать выхода за пределы контекста. В приведенном выше примере показан случай статической ссылки, но внутренние классы и их неявная ссылка на внешний класс могут быть одинаково опасными. Второе решение заключается в использовании контекста приложения. Этот контекст будет существовать до тех пор, пока ваше приложение живо и не зависит от жизненного цикла действий. Если вы планируете хранить долгоживущие объекты, которым нужен контекст, запомните объект приложения. Вы можете легко получить его, вызвав Context.getApplicationContext () или Activity.getApplication ().

Но для AlertDialog() ни getApplicationContext(), ни getApplication() не могут быть использованы в качестве контекста, так как выдает исключение:

«Невозможно добавить окно - нулевой токен не для приложения»

по ссылкам: 1 , 2 , 3 и т. Д.

Итак, действительно ли это следует считать «ошибкой», поскольку нам официально рекомендуется использовать Activity.getApplication(), и, тем не менее, он не работает так, как рекламируется?

Jim

Ответы [ 23 ]

1301 голосов
/ 29 августа 2011

Вместо getApplicationContext(), просто используйте ActivityName.this.

184 голосов
/ 21 июня 2012

Использование this не сработало для меня, но MyActivityName.this сработало.Надеюсь, это поможет любому, кто не смог заставить this работать.

56 голосов
/ 25 декабря 2014

Вы можете продолжать использовать getApplicationContext(), но перед использованием следует добавить этот флаг: dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT), и ошибка не будет отображаться.

Добавьте следующее разрешение в манифест:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
34 голосов
/ 26 июня 2011

Ваш диалог не должен быть «долгоживущим объектом, которому нужен контекст».Документация сбивает с толку.В основном, если вы делаете что-то вроде:

static Dialog sDialog;

(обратите внимание на static )

Затем в каком-то упражнении где-то вы сделали

 sDialog = new Dialog(this);

Youскорее всего, утечка исходной активности во время ротации или аналогичной, которая может разрушить активность.(Если вы не выполните очистку в onDestroy, но в этом случае вы, вероятно, не сделаете объект Dialog статическим)

Для некоторых структур данных имеет смысл сделать их статичными и основанными на контексте приложения, но обычноне для вещей, связанных с пользовательским интерфейсом, таких как диалоги.Итак, что-то вроде этого:

Dialog mDialog;

...

mDialog = new Dialog(this);

Хорошо и не должно пропускать действие, поскольку mDialog будет освобождено вместе с действием, поскольку оно не статично.

33 голосов
/ 08 октября 2016

Вы правильно определили проблему, когда сказали «... для AlertDialog () ни getApplicationContext (), ни getApplication () не могут быть использованы в качестве контекста, так как они выдают исключение:« Невозможно добавить window - token nullне для приложения ""

. Чтобы создать диалог, вам нужен контекст активности или контекст службы , а не контекст приложения (оба getApplicationContext () и getApplication () возвращают контекст приложения).

Вот как вы получаете Контекст действия :

(1) В действии или услуге:

AlertDialog.Builder builder = new AlertDialog.Builder(this);

(2) Во фрагменте: AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

Утечки памяти не являются проблемой, свойственной ссылке «это», котораяссылка объекта на себя (т. е. ссылка на фактическую выделенную память для хранения данных объекта).Это происходит с любой выделенной памятью, для которой сборщик мусора (GC) не может освободиться после того, как выделенная память пережила свой полезный срок службы.

Большую часть времени, когда переменная выходитвне области памяти будет восстановлено GC.Однако утечки памяти могут возникать, когда ссылка на объект, содержащийся в переменной, скажем «x», сохраняется даже после того, как объект пережил свой полезный срок службы.Таким образом, выделенная память будет потеряна до тех пор, пока «x» будет содержать ссылку на нее, поскольку GC не будет освобождать память до тех пор, пока на эту память все еще ссылаются.Иногда утечки памяти не очевидны из-за цепочки ссылок на выделенную память.В таком случае ГХ не освободит память до тех пор, пока не будут удалены все ссылки на эту память.

Чтобы предотвратить утечки памяти, проверьте свой код на наличие логических ошибок, которые приводят к тому, что на выделенную память будет ссылаться бесконечно "это "(или другие ссылки).Не забудьте также проверить ссылки на цепочки.Вот некоторые инструменты, которые вы можете использовать, чтобы помочь вам проанализировать использование памяти и обнаружить эти неприятные утечки памяти:

23 голосов
/ 10 сентября 2014

Мне пришлось отправить свой контекст через конструктор на настраиваемом адаптере, отображаемом во фрагменте, и у меня возникла эта проблема с getApplicationContext ().Я решил это с помощью:

this.getActivity().getWindow().getContext() в обратном вызове фрагментов onCreate.

22 голосов
/ 24 апреля 2018

in Activity просто используйте:

MyActivity.this

in Fragment:

getActivity();
19 голосов
/ 27 февраля 2013

В Activity при нажатии кнопки, показывающей диалоговое окно

Dialog dialog = new Dialog(MyActivity.this);

работал для меня.

18 голосов
/ 03 декабря 2012

Небольшой взлом: вы можете предотвратить разрушение вашей активности с помощью GC (вам не следует этого делать, но в некоторых ситуациях это может помочь. Не забудьте установить contextForDialog на null, когда он больше не нужен):

public class PostActivity extends Activity  {
    ...
    private Context contextForDialog = null;
    ...
    public void onCreate(Bundle savedInstanceState) {
        ...
        contextForDialog = this;
    }
    ...
    private void showAnimatedDialog() {
        mSpinner = new Dialog(contextForDialog);
        mSpinner.setContentView(new MySpinner(contextForDialog));
        mSpinner.show();
    }
    ...
}
14 голосов
/ 20 октября 2018

***** kotlin version *****

Вы должны передать this@YourActivity вместо applicationContext или baseContext

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...