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


665

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

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

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

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

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

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

по ссылкам: 1 , 2 , 3 и др.

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

Джим


ссылка на первый пункт, где Р. Гай советует использовать getApplication: android-developers.blogspot.com/2009/01/…
gymshoe

другие ссылки: stackoverflow.com/questions/1561803/…
gymshoe

1
другая ссылка: stackoverflow.com/questions/2634991/…
gymshoe


Ответы:


1354

Вместо того getApplicationContext(), чтобы просто использовать ActivityName.this.


67
Большой! Просто чтобы прокомментировать это ... вам иногда может понадобиться хранить «this» глобально (например), чтобы получить доступ к нему в реализованном методе слушателя, который имеет свое собственное «this». В этом случае вы должны определить «контекст контекста» глобально, а затем в onCreate установить «контекст = это», а затем обратиться к «контексту». Надеюсь, это тоже пригодится.
Стивен Л

8
На самом деле, так как Listenerуроки часто анонимны, я просто стараюсь, final Context ctx = this;и я ухожу;)
Алекс

28
@StevenL Чтобы делать то, что вы говорите, вы должны использовать ExternalClassName.this, чтобы явно ссылаться на «this» внешнего класса.
Артем Руссаковский

11
Не утечет ли использование «this», если ваш диалог используется в обратном вызове, и вы покидаете действие до вызова обратного вызова? По крайней мере, это то, на что Android жалуется в logcat.
Артем Руссаковский

6
Я бы не советовал подход @StevenLs, так как вы можете легко потерять память об этом действии, если не забудете очистить статическую ссылку в onDestroy - Артем прав. Подход StevenLs обусловлен отсутствием понимания того, как работает Java
Дори,

192

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


63
Вот что происходит, когда вы используете thisизнутри внутреннего класса. Если вы хотите сослаться на экземпляр внешнего класса, вы должны указать это, как вы делаете с OuterClass.this. Просто использование thisвсегда ссылается на самый внутренний экземпляр класса.
Кака

60

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

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

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

1
Я не могу добавить окно android.view.ViewRootImpl$W@426ce670 - в этом типе окна отказано в разрешении
Ram G.

разрешение на добавление: <использование-разрешения android: name = "android.permission.SYSTEM_ALERT_WINDOW" />
codezjx

3
Похоже, что вы не можете включить это разрешение в API 23 и далее code.google.com/p/android-developer-preview/issues/…
Рой Чжан

1
Вы можете использовать его для API 23 и выше, однако вам нужно предложить пользователю: startActivityForResult (new Intent (Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse ("package:" + getPackageName ())), OVERLAY_PERMISSION_REQ_CODE); Однако, как вы должны использовать это другое дело ...
Бен Neill

2
Это полезно, когда вы показываете диалог прогресса внутри сервиса
Anand Savjani

37

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


Для Activity вы также можете использовать ActivityName.this, где ActivityName - это (очевидно) имя вашей деятельности (например, MainActivity)
Луис Кабрера Бенито

34

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

static Dialog sDialog;

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

Затем в деятельности где-то вы сделали

 sDialog = new Dialog(this);

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

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

Dialog mDialog;

...

mDialog = new Dialog(this);

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


я звоню из асинктаск, это сработало для меня,
спасибо

мой диалог был статическим, как только я удалил статическое объявление, оно работало.
Седди Мухоза

25

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

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


4
Это также сработало для меня, я передал его конструктору внешнего AsyncTask, который я использую (он показывает диалог прогресса).
Рохан Кандвал

3
это
РЕАЛЬНЫЙ

1
Я согласен с @teejay
Эрди Изги


20

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

Dialog dialog = new Dialog(MyActivity.this);

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


19

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

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


18

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

@MurtuzaKabul Это работает, потому что это == PostActivity, которое наследует от Activity->, которое наследует от Context, поэтому, когда вы пропускаете диалог своего контекста, вы фактически передаете действие
Elad Gelman

13

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

нравится

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

12

Просто используйте следующее:

ДЛЯ ПОЛЬЗОВАТЕЛЕЙ JAVA

Если вы используете активность -> AlertDialog.Builder builder = new AlertDialog.Builder(this);

ИЛИ

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

Если вы используете фрагмент -> AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

ДЛЯ КОТЛИН ПОЛЬЗОВАТЕЛЕЙ

Если вы используете активность -> val builder = AlertDialog.Builder(this)

ИЛИ

val builder = AlertDialog.Builder(this@your_activity.this)

Если вы используете фрагмент -> val builder = AlertDialog.Builder(activity!!)


9

добавление

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

а также

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

Это работает для меня сейчас. После даже закрытия и открытия приложения выдали ошибку на тот момент.


9

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

Решение, которое работало для меня, должно было пройти getActivity(); т.е.

progressDialog = new ProgressDialog(getActivity());


6

использование MyDialog md = new MyDialog(MyActivity.this.getParent());


6

Если вы находитесь за пределами действия, то вам нужно использовать в своей функции "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);

5

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

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

Ура!


5

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

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

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


4

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

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


2

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

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

2

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

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


2

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


1

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

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


0

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

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

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

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

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

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

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

-1

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

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

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

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

Дэвид


-1
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();
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.