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

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

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

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

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

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

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

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

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

Джим

Ответ 1

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

Ответ 2

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

Ответ 3

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

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

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

Ответ 4

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

static Dialog sDialog;

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

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

 sDialog = new Dialog(this);

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

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

Dialog mDialog;

...

mDialog = new Dialog(this);

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

Ответ 5

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

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

MyActivity.this

в фрагменте:

getActivity();

Ответ 9

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

Dialog dialog = new Dialog(MyActivity.this);

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

Ответ 10

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

Ответ 11

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

Вы должны пройти [email protected] вместо applicationContext или baseContext

Ответ 12

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

как это

ProgressDialog pdialog;
pdialog = new ProgressDialog(getActivity());
pdialog.setCancelable(true);
pdialog.setMessage("Loading ....");
pdialog.show();

Ответ 13

добавление

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

и

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

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

Ответ 14

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

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

progressDialog = new ProgressDialog(getActivity());

Ответ 15

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

Ответ 16

Если вы находитесь за пределами 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);

Ответ 17

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

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

Ура!

Ответ 18

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

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

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

Ответ 19

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

public void show() {
    if(mContext instanceof Activity) {
        Activity activity = (Activity) mContext;
        if (!activity.isFinishing() && !activity.isDestroyed()) {
            dialog.show();
        }
    }
}

Ответ 20

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

this.getContext();

Ответ 21

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

Ответ 22

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

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

Ответ 23

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

final Dialog dialog = new Dialog(new ContextThemeWrapper(
            this, R.style.MyThemeDialog));

Ответ 24

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

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

Ответ 25

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

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

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

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

Дэвид

Ответ 26

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

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

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

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

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

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

            AlertDialog.Builder alert = new AlertDialog.Builder(activity);

Ответ 27

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();