it-swarm.com.ru

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

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

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

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

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

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

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

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

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

Джим

635
gymshoe

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

1296
Steven L

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

184
TrueCoke

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

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

<uses-permission Android:name="Android.permission.SYSTEM_ALERT_WINDOW" />
56
codezjx

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

static Dialog sDialog;

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

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

 sDialog = new Dialog(this);

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

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

Dialog mDialog;

...

mDialog = new Dialog(this);

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

34
Kevin TeslaCoil

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

33
ONE

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

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

23
Grux

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

MyActivity.this

в Фрагмент:

getActivity();
21
Mahmoud Ayman

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

Dialog dialog = new Dialog(MyActivity.this);

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

19
P_Pran

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

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

как это

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

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

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

12
MilaDroid

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

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

progressDialog = new ProgressDialog(getActivity());

9
T.M

добавление

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

а также

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

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

9
AlphaStack

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

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

6
MSA

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

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

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

5
Engin OZTURK

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

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

Ура!

5
curlyreggie

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

public void show() {
    if(mContext instanceof Activity) {
        Activity activity = (Activity) mContext;
        if (!activity.isFinishing() && !activity.isDestroyed()) {
            dialog.show();
        }
    }
}
4
Денис Шовгеня

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

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

2
Erwan

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

2
Priyank Joshi

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

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

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

this.getContext();
2
theWalker

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

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

1
G_V