Вызов метаданных "Невозможно добавить нулевой токен окна - это не приложение" с getApplication() как контекст

Моя активность пытается создать AlertDialog, который требует контекста в качестве параметра. Это работает, как ожидается, если я использую:

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

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

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

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

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

за ссылки: 1, 2, 3 и т.д.

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

Джим

+646
26 апр. '11 в 21:21
источник поделиться
27 ответов

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

+1312
29 авг. '11 в 11:04
источник

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

+187
21 июн. '12 в 0:54
источник
другие ответы

Связанные вопросы


Похожие вопросы

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

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

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+56
25 дек. '14 в 8:09
источник

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

static Dialog sDialog;

(обратите внимание на статический)

Затем в действии где-то вы сделали

 sDialog = new Dialog(this);

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

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

Dialog mDialog;

...

mDialog = new Dialog(this);

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

+34
26 июн. '11 в 7:56
источник

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

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

Здесь вы можете получить Контекст активности:

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

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

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

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

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

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

+34
07 окт. '16 в 23:37
источник

Вы не можете отобразить приложение window/dialog через Контекст, который не является Activity или Service. Попробуйте передать действительную ссылку на действия

+28
09 июл. '15 в 11:56
источник

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

this.getActivity().getWindow().getContext() в обратном вызове < <21 > .

+23
10 сент. '14 в 1:41
источник

в Активности просто используйте:

MyActivity.this

в фрагменте:

getActivity();
+23
24 апр. '18 в 10:41
источник

В Activity при нажатии кнопки с диалоговым окном

Dialog dialog = new Dialog(MyActivity.this);

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

+20
27 февр. '13 в 11:26
источник

Небольшой взлом: вы можете предотвратить разрушение вашей активности с помощью 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();
    }
    ...
}
+18
03 дек. '12 в 10:03
источник

***** Котлин версия *****

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

+15
20 окт. '18 в 11:47
источник

Если вы используете фрагмент и используете сообщение AlertDialog/Toast, используйте параметр getActivity() в параметре контекста.

как это

ProgressDialog pdialog;
pdialog = new ProgressDialog(getActivity());
pdialog.setCancelable(true);
pdialog.setMessage("Loading ....");
pdialog.show();
+13
23 нояб. '13 в 15:43
источник

добавление

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

и

"android.permission.SYSTEM_ALERT_WINDOW"/> в манифесте

Теперь он работает для меня. После того, как вы закрыли и открыли приложение, я дал ошибку в то время.

+9
21 апр. '15 в 11:06
источник

Я использовал ProgressDialog в фрагменте и получал эту ошибку при передаче getActivity().getApplicationContext() в качестве параметра конструктора. Изменение его на getActivity().getBaseContext() тоже не сработало.

Решение, которое работало для меня, состояло в передаче getActivity(); то есть.

progressDialog = new ProgressDialog(getActivity());

+9
04 нояб. '16 в 14:18
источник

Используйте MyDialog md = new MyDialog(MyActivity.this.getParent());

+6
01 мая '13 в 7:42
источник

Если вы находитесь за пределами Activity, вам необходимо использовать в своей функции "NameOfMyActivity.this" как активность активности, например:

public static void showDialog(Activity activity) {
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setMessage("Your Message")
        .setPositiveButton("Yes", dialogClickListener)
        .setNegativeButton("No", dialogClickListener).show();
}


//Outside your Activity
showDialog(NameOfMyActivity.this);
+6
01 июл. '14 в 22:29
источник

Если вы используете фрагмент и используете сообщение AlertDialog / Toast, используйте getActivity() в параметре контекста.

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

Ура!

+5
12 янв. '13 в 4:05
источник

Попробуйте использовать контекст активности, который будет находиться под диалогом. Но будьте осторожны, если вы используете ключевое слово "this", потому что оно не будет работать каждый раз.

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

Я не нашел эту информацию из какого-либо документа, но попытался. Это мое решение без сильного фона. Если кто-нибудь с более известными, не стесняйтесь комментировать.

+5
22 авг. '13 в 8:33
источник

Для будущих читателей это должно помочь:

public void show() {
    if(mContext instanceof Activity) {
        Activity activity = (Activity) mContext;
        if (!activity.isFinishing() && !activity.isDestroyed()) {
            dialog.show();
        }
    }
}
+4
20 сент. '16 в 14:36
источник

В моем случае работа:

this.getContext();
+2
29 нояб. '12 в 12:07
источник

Попробуйте getParent() в контексте контекста аргумента, как новый AlertDialog.Builder(getParent()); Надеюсь, это сработает, у меня это сработало.

+2
05 февр. '13 в 7:27
источник

Я думаю, что это также может произойти, если вы пытаетесь показать диалог из потока, который не является основным потоком пользовательского интерфейса.

Используйте runOnUiThread() в этом случае.

+2
17 апр. '13 в 9:28
источник

Или другая возможность - создать диалог следующим образом:

final Dialog dialog = new Dialog(new ContextThemeWrapper(
            this, R.style.MyThemeDialog));
+2
01 дек. '15 в 7:11
источник

После просмотра API вы можете передать диалог своей активности или getActivity, если вы находитесь в фрагменте, а затем принудительно очистите его с помощью dialog.dismiss() в методах возврата для предотвращения утечек.

Хотя это явно не указано нигде, я знаю, кажется, что вы передали обратно диалог в OnClickHandlers только для этого.

+1
16 дек. '14 в 13:27
источник

Вот как я решил ту же ошибку для своего приложения:
Добавив следующую строку после создания диалога:

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);  

Вам не нужно приобретать контекст. Это особенно полезно, если вы открываете другой диалог поверх текущего всплывающего диалога. Или когда не удобно получать контекст.

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

Дэвид

0
05 авг. '19 в 22:33
источник

Если ваш диалог создается на адаптере:

Передайте упражнение конструктору адаптера:

adapter = new MyAdapter(getActivity(),data);

Получите через адаптер:

 public MyAdapter(Activity activity, List<Data> dataList){
       this.activity = activity;
    }

Теперь вы можете использовать на вашем Builder

            AlertDialog.Builder alert = new AlertDialog.Builder(activity);
0
14 авг. '19 в 23:53
источник
android.support.v7.app.AlertDialog.Builder builder = new android.support.v7.app.AlertDialog.Builder(getWindow().getDecorView().getRootView().getContext());

builder.setTitle("Confirm");
builder.setMessage("Are you sure you want delete your old account?");

builder.setPositiveButton("YES", new DialogInterface.OnClickListener() {

    public void onClick(DialogInterface dialog, int which) {
        //Do nothing but close the dialog



        dialog.dismiss();

    }
});

builder.setNegativeButton("NO", new DialogInterface.OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {

        //Do nothing
        dialog.dismiss();
    }
});

android.support.v7.app.AlertDialog alert = builder.create();
alert.show();
0
18 сент. '19 в 5:27
источник

Посмотрите другие вопросы по меткам или Задайте вопрос