Zgłaszanie okna dialogowego „Nie można dodać okna - token null nie jest dla aplikacji” z getApplication () jako kontekstem


665

Moja aktywność próbuje utworzyć AlertDialog, który wymaga kontekstu jako parametru. Działa to zgodnie z oczekiwaniami, jeśli użyję:

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

Jestem jednak ostrożny w używaniu „tego” jako kontekstu ze względu na możliwość wycieków pamięci, gdy Aktywność zostanie zniszczona i odtworzona nawet podczas czegoś prostego, takiego jak obracanie ekranu. Z pokrewnego postu na blogu programisty Androida :

Istnieją dwa proste sposoby uniknięcia wycieków pamięci kontekstowej. Najbardziej oczywistym z nich jest unikanie ucieczki z kontekstu poza własnym zasięgiem. Powyższy przykład pokazuje przypadek odniesienia statycznego, ale klasy wewnętrzne i ich ukryte odniesienie do klasy zewnętrznej mogą być równie niebezpieczne. Drugim rozwiązaniem jest użycie kontekstu aplikacji. Ten kontekst będzie istniał tak długo, jak długo aplikacja będzie żyła i nie będzie zależeć od cyklu życia działań. Jeśli planujesz zachować obiekty o długiej żywotności, które potrzebują kontekstu, zapamiętaj obiekt aplikacji. Możesz go łatwo uzyskać, wywołując Context.getApplicationContext () lub Activity.getApplication ().

Ale dla AlertDialog()żadnego z nich getApplicationContext()lub getApplication()jest akceptowalny jako kontekst, ponieważ rzuca wyjątek:

„Nie można dodać okna - token null nie jest przeznaczony dla aplikacji”

według referencji: 1 , 2 , 3 itd.

Czy to naprawdę powinno być uważane za „błąd”, skoro oficjalnie zaleca się stosowanie, Activity.getApplication()a mimo to nie działa tak, jak w reklamie?

Jim


odniesienie do pierwszego elementu, w którym R.Guy radzi za pomocą getApplication: android-developers.blogspot.com/2009/01/…
gymshoe


1
inne
numery


Odpowiedzi:


1354

Zamiast tego po getApplicationContext()prostu użyj ActivityName.this.


67
Świetny! Aby to skomentować… czasami może być konieczne globalne przechowywanie „tego” (na przykład) w celu uzyskania dostępu do metody zaimplementowanej przez słuchacza, który ma własne „to”. W takim przypadku należałoby zdefiniować „kontekst kontekstowy” globalnie, a następnie w onCreate ustawić „kontekst = to”, a następnie odwołać się do „kontekstu”. Przydaje się też nadzieja.
Steven L,

8
W rzeczywistości, ponieważ Listenerzajęcia są często anonimowo-wewnętrzne, zwykle tak robię final Context ctx = this;i nie ma mnie;)
Alex

28
@StevenL Aby zrobić to, co mówisz, powinieneś użyć ExternalClassName.this, aby wyraźnie odnieść się do „tego” klasy zewnętrznej.
Artem Russakovskii

11
Czy nie użyłbyś tego „wycieku”, gdyby twoje okno dialogowe było używane w wywołaniu zwrotnym i porzuciłeś działanie przed wywołaniem wywołania zwrotnego? Przynajmniej na to Android wydaje się narzekać w logcat.
Artem Russakovskii

6
Nie radziłbym podejścia @StevenLs, ponieważ możesz łatwo wyciec pamięć tego działania, chyba że pamiętasz wyczyszczenie statycznego odwołania w onDestroy - Artem jest poprawny. Podejście StevenLsa wynika z braku zrozumienia, jak działa Java
Dori,

191

Korzystanie thisnie działało dla mnie, ale działało MyActivityName.this. Mam nadzieję, że pomoże to każdemu, kto nie mógł dostać się thisdo pracy.


63
Tak się dzieje, gdy używasz thisz wewnętrznej klasy. Jeśli chcesz odwoływać się do wystąpienia klasy zewnętrznej, musisz to określić, tak jak to robisz OuterClass.this. Tylko użycie thiszawsze odwołuje się do najbardziej wewnętrznej instancji klasy.
kaka

60

Możesz nadal używać getApplicationContext(), ale przed użyciem powinieneś dodać tę flagę:, dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)a błąd się nie pojawi.

Dodaj następujące pozwolenie do swojego manifestu:

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

1
Nie mogę dodać okna android.view.ViewRootImpl$W@426ce670 - odmowa dostępu dla tego typu okna
Ram G.

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

3
Wygląda na to, że nie możesz włączyć tego uprawnienia w interfejsie API 23 i code.google.com/p/android-developer-preview/issues/…
roy zhang

1
Możesz go używać w interfejsie API 23 i nowszych, jednak musisz zapytać użytkownika: startActivityForResult (nowa intencja (Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse („pakiet:” + getPackageName ())), OVERLAY_PERMISSION_REQ_CODE); jednak to, czy powinieneś go użyć, to inna sprawa ...
Ben Neill

2
Jest to przydatne, gdy wyświetlasz okno postępu w usłudze
Anand Savjani,

37

Prawidłowo zidentyfikowałeś problem, gdy powiedziałeś „... dla AlertDialog () ani getApplicationContext (), ani getApplication () nie są akceptowane jako kontekst, ponieważ zgłasza wyjątek:„ Nie można dodać okna - token null nie jest dla Aplikacja'"

Aby utworzyć okno dialogowe, potrzebujesz kontekstu działania lub kontekstu usługi , a nie kontekstu aplikacji (zarówno getApplicationContext (), jak i getApplication () zwracają kontekst aplikacji).

Oto jak uzyskać kontekst działania :

(1) W działaniu lub usłudze:

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

(2) we fragmencie: AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

Wycieki pamięci nie stanowią problemu nieodłącznie związanego z odniesieniem „to”, które jest odniesieniem obiektu do siebie (tj. Odniesieniem do faktycznej przydzielonej pamięci do przechowywania danych obiektu). Dzieje się tak z każdą przydzieloną pamięcią, dla której Garbage Collector (GC) nie jest w stanie zwolnić po tym, jak przydzielona pamięć przeżyje swój okres użytkowania.

Przez większość czasu, gdy zmienna wykracza poza zakres, pamięć zostanie odzyskana przez GC. Jednak wycieki pamięci mogą wystąpić, gdy odwołanie do obiektu przechowywanego przez zmienną, powiedz „x”, utrzymuje się nawet po upływie okresu użyteczności obiektu. Przydzielona pamięć zostanie zatem utracona, dopóki „x” będzie zawierało odniesienie do niej, ponieważ GC nie zwolni pamięci, dopóki pamięć ta będzie nadal wskazywana. Czasami wycieki pamięci nie są widoczne z powodu łańcucha odniesień do przydzielonej pamięci. W takim przypadku GC nie zwolni pamięci, dopóki wszystkie odniesienia do tej pamięci nie zostaną usunięte.

Aby zapobiec wyciekom pamięci, sprawdź kod pod kątem błędów logicznych, które powodują, że do przydzielonej pamięci odwołuje się w nieskończoność „to” (lub inne odniesienia). Pamiętaj, aby sprawdzić również odniesienia do łańcucha. Oto niektóre narzędzia, których możesz użyć, aby przeanalizować wykorzystanie pamięci i znaleźć te nieznośne wycieki pamięci:


W przypadku działania możesz również użyć ActivityName. To gdzie ActivityName to (oczywiście) nazwa twojej aktywności (na przykład MainActivity)
Luis Cabrera Benito

34

Twoje okno dialogowe nie powinno być „długotrwałym obiektem, który potrzebuje kontekstu”. Dokumentacja jest myląca. Zasadniczo, jeśli robisz coś takiego:

static Dialog sDialog;

(zwróć uwagę na statyczne )

Potem, gdzieś to zrobiłeś

 sDialog = new Dialog(this);

Prawdopodobnie wycieknie pierwotna aktywność podczas rotacji lub podobnej czynności, która mogłaby ją zniszczyć. (Chyba że wyczyścisz w onDestroy, ale w takim przypadku prawdopodobnie nie spowodowałbyś, aby obiekt Dialog był statyczny)

W przypadku niektórych struktur danych sensowne byłoby uczynienie ich statycznymi i opartymi na kontekście aplikacji, ale generalnie nie dla rzeczy związanych z interfejsem użytkownika, takich jak okna dialogowe. Więc coś takiego:

Dialog mDialog;

...

mDialog = new Dialog(this);

Jest w porządku i nie powinien przeciekać aktywności, ponieważ mDialog zostałby zwolniony z działalnością, ponieważ nie jest statyczny.


nazywam to z asynchronicznego zadania, to zadziałało dla mnie, kolego
MemLeak

moje okno dialogowe było statyczne, gdy usunąłem deklarację statyczną, zadziałało.
Ceddy Muhoza

25

Musiałem wysłać mój kontekst za pomocą konstruktora na niestandardowym adapterze wyświetlanym w fragmencie i miałem ten problem z getApplicationContext (). Rozwiązałem to za pomocą:

this.getActivity().getWindow().getContext()w onCreatewywołaniu zwrotnym fragmentów .


4
To zadziałało również dla mnie, przekazałem je konstruktorowi zewnętrznego AsyncTask, którego używam (pokazuje okno dialogowe postępu).
Rohan Kandwal

3
oto PRAWDZIWA odpowiedź na bardziej złożone zadania :)
teejay,

1
Zgadzam się z @teejay
Erdi İzgi

23

w działaniu po prostu użyj:

MyActivity.this

we fragmencie:

getActivity();

Naprawiłem to w mojej działalności. Dzięki
Werner

20

W Activitypo kliknięciu przycisku pokazującego okno dialogowe

Dialog dialog = new Dialog(MyActivity.this);

Pracował dla mnie.


19

***** wersja kotlin *****

Powinieneś przekazać this@YourActivityzamiast applicationContextlubbaseContext


18

Trochę Hack: można zapobiec niszczeniu swoją aktywność przez GC (nie powinno się to zrobić, ale może pomóc w niektórych sytuacjach nie zapomnij ustawić. contextForDialogDo nullkiedy to już nie jest potrzebne):

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 Działa ponieważ == PostActivity która dziedziczy z zadaniowego> która dziedziczy z kontekstu, więc kiedy przechodzą oknie dialogowym kontekstu są faktycznie przechodzącą aktywności
elad Gelman

13

Jeśli używasz fragmentu i używasz komunikatu AlertDialog / Toast, użyj getActivity () w parametrze kontekstu.

lubię to

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

12

Wystarczy użyć następujących:

DLA UŻYTKOWNIKÓW JAVA

W przypadku korzystania z aktywności -> AlertDialog.Builder builder = new AlertDialog.Builder(this);

LUB

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

W przypadku korzystania z fragmentu -> AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

DLA UŻYTKOWNIKÓW KOTLIN

W przypadku korzystania z aktywności -> val builder = AlertDialog.Builder(this)

LUB

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

W przypadku korzystania z fragmentu -> val builder = AlertDialog.Builder(activity!!)


9

dodawanie

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

i

"android.permission.SYSTEM_ALERT_WINDOW"/> w manifestie

Teraz działa dla mnie. Po nawet zamknięciu i otwarciu aplikacji, dał mi wtedy błąd.


9

Używałem ProgressDialogfragmentu i otrzymywałem ten błąd podczas przekazywania getActivity().getApplicationContext()jako parametru konstruktora. Zmiana go getActivity().getBaseContext()również nie działała.

Rozwiązaniem, które działało dla mnie, było przejście getActivity(); to znaczy

progressDialog = new ProgressDialog(getActivity());


6

Posługiwać się MyDialog md = new MyDialog(MyActivity.this.getParent());


6

Jeśli jesteś poza działaniem, musisz użyć w funkcji „NameOfMyActivity.this” jako działania, na przykład:

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

Jeśli używasz fragmentu i AlertDialog / Toastwiadomości, użyj getActivity()w parametrze kontekstu.

Pracował dla mnie.

Twoje zdrowie!


5

Spróbuj użyć kontekstu działania, które będzie w oknie dialogowym. Należy jednak zachować ostrożność, używając słowa kluczowego „to”, ponieważ nie będzie ono działać za każdym razem.

Na przykład, jeśli masz TabActivity jako host z dwiema kartami, a każda karta jest innym działaniem, a jeśli spróbujesz utworzyć okno dialogowe z jednej z kart (działań) i jeśli użyjesz „tego”, otrzymasz wyjątek, w tym okno dialogowe sprawy powinno być połączone z działaniem hosta, które hostuje wszystko i jest widoczne. (możesz powiedzieć najbardziej widoczny kontekst działania rodzica)

Nie znalazłem tych informacji w żadnym dokumencie, ale próbowałem. To jest moje rozwiązanie bez silnego zaplecza. Jeśli ktoś ma większą wiedzę, nie wahaj się komentować.


4

Dla przyszłych czytelników powinno to pomóc:

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


2

Lub inną możliwością jest utworzenie okna dialogowego w następujący sposób:

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

2

Myślę, że może się to zdarzyć, jeśli próbujesz wyświetlić okno dialogowe z wątku, który nie jest głównym wątkiem interfejsu użytkownika.

Użyj runOnUiThread()w takim przypadku.


2

Spróbuj getParent()w miejscu argumentu kontekstu, jak nowa AlertDialog.Builder(getParent());Mam nadzieję, że zadziała, zadziałało dla mnie.


1

Po przyjrzeniu się interfejsowi API możesz przekazać okno swojej aktywności lub getActivity, jeśli jesteś we fragmencie, a następnie zdecydowanie wyczyść go za pomocą metody dialog.dismiss () w metodach powrotu, aby zapobiec wyciekom.

Chociaż nigdzie nie jest to wyraźnie określone, wydaje się, że przywrócono okno dialogowe OnClickHandlers tylko po to, aby to zrobić.


0

Jeśli okno dialogowe tworzy się na adapterze:

Przekaż działanie konstruktorowi adaptera:

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

Odbierz na adapterze:

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

Teraz możesz używać w swoim Konstruktorze

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

-1

Oto jak rozwiązałem ten sam błąd dla mojej aplikacji:
Dodanie następującego wiersza po utworzeniu okna dialogowego:

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

Nie musisz pozyskiwać kontekstu. Jest to szczególnie przydatne, jeśli wyskakuje inne okno dialogowe nad bieżącym wyskakującym oknem dialogowym. Lub gdy uzyskanie kontekstu nie jest wygodne.

Mam nadzieję, że pomoże Ci to w rozwoju aplikacji.

David


-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();
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.