Dialog mit "getApplication () als Kontext" Fenster kann nicht hinzugefügt werden - Token null ist nicht für eine Anwendung "


665

Meine Aktivität versucht, einen AlertDialog zu erstellen, für den ein Kontext als Parameter erforderlich ist. Dies funktioniert wie erwartet, wenn ich Folgendes verwende:

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

Ich bin jedoch misstrauisch, "dies" als Kontext zu verwenden, da möglicherweise Speicherlecks auftreten, wenn Aktivität zerstört und neu erstellt wird, selbst während einer einfachen Bildschirmdrehung. Aus einem verwandten Beitrag im Blog des Android-Entwicklers :

Es gibt zwei einfache Möglichkeiten, um kontextbezogene Speicherverluste zu vermeiden. Am naheliegendsten ist es, zu vermeiden, dass der Kontext außerhalb seines eigenen Bereichs entkommt. Das obige Beispiel zeigte den Fall einer statischen Referenz, aber innere Klassen und ihre implizite Referenz auf die äußere Klasse können ebenso gefährlich sein. Die zweite Lösung besteht darin, den Anwendungskontext zu verwenden. Dieser Kontext bleibt so lange bestehen, wie Ihre Anwendung aktiv ist, und hängt nicht vom Lebenszyklus der Aktivitäten ab. Wenn Sie langlebige Objekte behalten möchten, die einen Kontext benötigen, merken Sie sich das Anwendungsobjekt. Sie können es einfach erhalten, indem Sie Context.getApplicationContext () oder Activity.getApplication () aufrufen.

Aber für das AlertDialog()weder getApplicationContext()oder getApplication()als Kontext akzeptabel, da es die Ausnahme auslöst:

"Fenster kann nicht hinzugefügt werden - Token null ist nicht für eine Anwendung geeignet"

gemäß Referenzen: 1 , 2 , 3 usw.

Sollte dies wirklich als "Fehler" angesehen werden, da uns offiziell empfohlen wird, ihn zu verwenden, Activity.getApplication()und er dennoch nicht wie angekündigt funktioniert?

Jim


Referenz für den ersten Artikel, bei dem R.Guy die Verwendung von getApplication empfiehlt: android-developers.blogspot.com/2009/01/…
gymshoe




Antworten:


1354

getApplicationContext()Verwenden Sie stattdessen einfach ActivityName.this.


67
Großartig! Um dies zu kommentieren, müssen Sie manchmal "dies" global speichern (zum Beispiel), um innerhalb der implementierten Methode eines Hörers, der über ein eigenes "dies" verfügt, darauf zuzugreifen. In diesem Fall würden Sie "Kontextkontext" global definieren und dann in onCreate "context = this" festlegen und dann auf "context" verweisen. Hoffe das ist auch nützlich.
Steven L

8
Eigentlich, da der ListenerUnterricht oft anonym-inner ist, mache final Context ctx = this;ich das einfach und bin weg;)
Alex

28
@StevenL Um das zu tun, was Sie sagen, sollten Sie ExternalClassName.this verwenden, um explizit auf "this" der äußeren Klasse zu verweisen.
Artem Russakovskii

11
Würde "this" nicht auslaufen, wenn Ihr Dialog in einem Rückruf verwendet wird und Sie die Aktivität verlassen, bevor der Rückruf aufgerufen wird? Zumindest scheint sich Android in logcat darüber zu beschweren.
Artem Russakovskii

6
Ich würde @StevenLs Ansatz nicht empfehlen, da Sie leicht den Speicher dieser Aktivität verlieren können, wenn Sie nicht daran denken, die statische Referenz in onDestroy zu löschen - Artem ist korrekt. StevenLs Ansatz beruht auf einem Mangel an Verständnis dafür, wie Java funktioniert
Dori

191

Die Verwendung thishat bei mir nicht funktioniert, aber MyActivityName.thisfunktioniert. Hoffe das hilft jedem, der nicht thiszur Arbeit kommen konnte.


63
Das passiert, wenn Sie thisinnerhalb einer inneren Klasse arbeiten. Wenn Sie auf die Instanz einer äußeren Klasse verweisen möchten, müssen Sie dies wie bei angeben OuterClass.this. Nur mit thisimmer auf die Instanz der innersten Klasse verweisen.
Kaka

60

Sie können weiterhin verwenden getApplicationContext(), aber vor der Verwendung sollten Sie dieses Flag hinzufügen: dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)und der Fehler wird nicht angezeigt.

Fügen Sie Ihrem Manifest die folgende Berechtigung hinzu:

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

1
Ich kann das Fenster android.view.ViewRootImpl$W@426ce670 nicht hinzufügen - Berechtigung für diesen Fenstertyp verweigert
Ram G.

Berechtigung hinzufügen: <Verwendungsberechtigung android: name = "android.permission.SYSTEM_ALERT_WINDOW" />
codezjx


1
Sie können es ab API 23 verwenden, müssen jedoch den Benutzer dazu auffordern: startActivityForResult (new Intent (Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse ("package:" + getPackageName ())), OVERLAY_PERMISSION_REQ_CODE); ob Sie es verwenden sollten , ist eine andere Sache ...
Ben Neill

2
Dies ist nützlich, wenn Sie den Fortschrittsdialog innerhalb des Dienstes
anzeigen

37

Sie haben das Problem korrekt identifiziert, als Sie sagten: "... für AlertDialog () ist weder getApplicationContext () noch getApplication () als Kontext akzeptabel, da die Ausnahme ausgelöst wird: 'Fenster kann nicht hinzugefügt werden - Token-Null ist nicht für eine Bewerbung'"

So erstellen Sie einen Dialog, benötigen Sie eine Aktivität Kontext oder einen Dienst - Kontext , kein Anwendungskontext (beide getApplicationContext () und GetApplication () geben einen Anwendungskontext).

So erhalten Sie den Aktivitätskontext :

(1) In einer Aktivität oder einem Dienst:

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

(2) In einem Fragment: AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

Speicherlecks sind kein Problem, das der "this" -Referenz innewohnt, die die Referenz eines Objekts auf sich selbst ist (dh die Referenz auf den tatsächlich zugewiesenen Speicher zum Speichern der Objektdaten). Dies geschieht mit jedem zugewiesenen Speicher, für den der Garbage Collector (GC) nicht freigeben kann, nachdem der zugewiesene Speicher seine Nutzungsdauer überschritten hat.

Wenn eine Variable den Gültigkeitsbereich verlässt, wird der Speicher meistens vom GC zurückgefordert. Speicherverluste können jedoch auftreten, wenn der Verweis auf ein Objekt, das von einer Variablen, z. B. "x", gehalten wird, auch dann bestehen bleibt, wenn das Objekt seine Nutzungsdauer überschritten hat. Der zugewiesene Speicher geht daher verloren, solange "x" einen Verweis darauf enthält, da GC den Speicher nicht freigibt, solange auf diesen Speicher noch verwiesen wird. Manchmal sind Speicherlecks aufgrund einer Kette von Verweisen auf den zugewiesenen Speicher nicht erkennbar . In einem solchen Fall gibt der GC den Speicher erst frei, wenn alle Verweise auf diesen Speicher entfernt wurden.

Um Speicherlecks zu vermeiden, überprüfen Sie Ihren Code auf logische Fehler, die dazu führen, dass der zugewiesene Speicher durch "this" (oder andere Referenzen) auf unbestimmte Zeit referenziert wird. Denken Sie auch daran, nach Kettenreferenzen zu suchen. Hier sind einige Tools, mit denen Sie die Speichernutzung analysieren und diese lästigen Speicherlecks finden können:


Für eine Aktivität können Sie auch ActivityName verwenden. Hier ist ActivityName (offensichtlich) der Name Ihrer Aktivität (zum Beispiel MainActivity)
Luis Cabrera Benito

34

Ihr Dialog sollte kein "langlebiges Objekt sein, das einen Kontext benötigt". Die Dokumentation ist verwirrend. Grundsätzlich, wenn Sie etwas tun wie:

static Dialog sDialog;

(Beachten Sie die statische )

Dann in einer Aktivität irgendwo, die Sie getan haben

 sDialog = new Dialog(this);

Sie würden wahrscheinlich die ursprüngliche Aktivität während einer Rotation oder ähnlichem verlieren, was die Aktivität zerstören würde. (Es sei denn, Sie bereinigen in onDestroy, aber in diesem Fall würden Sie das Dialog-Objekt wahrscheinlich nicht statisch machen.)

Für einige Datenstrukturen wäre es sinnvoll, sie statisch zu machen und auf dem Kontext der Anwendung zu basieren, im Allgemeinen jedoch nicht für UI-bezogene Dinge wie Dialoge. Also so etwas wie das:

Dialog mDialog;

...

mDialog = new Dialog(this);

Ist in Ordnung und sollte die Aktivität nicht verlieren, da mDialog mit der Aktivität freigegeben würde, da sie nicht statisch ist.


Ich rufe es von einer Asynctask an, das hat bei mir funktioniert, danke Kumpel
MemLeak

Mein Dialog war statisch, nachdem ich die statische Deklaration entfernt hatte, funktionierte es.
Ceddy Muhoza

25

Ich musste meinen Kontext über einen Konstruktor auf einem benutzerdefinierten Adapter senden, der in einem Fragment angezeigt wurde, und hatte dieses Problem mit getApplicationContext (). Ich habe es gelöst mit:

this.getActivity().getWindow().getContext()im onCreateRückruf der Fragmente .


4
Dies hat auch bei mir funktioniert. Ich habe es an den Konstruktor der externen AsyncTask übergeben, die ich verwende (es zeigt einen Fortschrittsdialog).
Rohan Kandwal

3
Dies ist die echte Antwort für komplexere Aufgaben :)
Teejay

1
Ich bin mit dem @teejay
Erdi İzgi

23

Verwenden Sie in Aktivität einfach:

MyActivity.this

im Fragment:

getActivity();

Dies hat es für mich in meiner Aktivität behoben. Danke
Werner

20

In Activityauf Klicken der Schaltfläche , um ein Dialogfeld zeigt

Dialog dialog = new Dialog(MyActivity.this);

Hat für mich gearbeitet.


19

***** Kotlin-Version *****

Sie sollten this@YourActivityanstelle von applicationContextoder bestehenbaseContext


18

Kleiner Hack: Sie können verhindern, dass Ihre Aktivität durch GC zerstört wird (Sie sollten dies nicht tun, aber es kann in einigen Situationen hilfreich sein. Vergessen Sie nicht, die Einstellung festzulegen contextForDialog, nullwenn sie nicht mehr benötigt wird):

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 Es funktioniert, weil diese == PostActivity, die von Activity-> erbt, die von Context erbt. Wenn Sie also den Dialog in Ihrem Kontext übergeben, übergeben Sie tatsächlich die Aktivität
Elad Gelman

13

Wenn Sie ein Fragment verwenden und die AlertDialog / Toast-Nachricht verwenden, verwenden Sie getActivity () im Kontextparameter.

so was

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

12

Verwenden Sie einfach Folgendes:

FÜR JAVA-BENUTZER

Falls Sie Aktivität verwenden -> AlertDialog.Builder builder = new AlertDialog.Builder(this);

ODER

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

Falls Sie Fragment verwenden -> AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

FÜR KOTLIN-BENUTZER

Falls Sie Aktivität verwenden -> val builder = AlertDialog.Builder(this)

ODER

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

Falls Sie Fragment verwenden -> val builder = AlertDialog.Builder(activity!!)


9

Hinzufügen

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

und

"android.permission.SYSTEM_ALERT_WINDOW"/> im Manifest

Es funktioniert jetzt für mich. Nachdem ich die Anwendung sogar geschlossen und geöffnet hatte, gab mir der Fehler zu diesem Zeitpunkt.


9

Ich habe ProgressDialogin einem Fragment verwendet und diesen Fehler beim Übergeben getActivity().getApplicationContext()als Konstruktorparameter erhalten. Ändern ingetActivity().getBaseContext() hat auch nicht funktioniert.

Die Lösung, die für mich funktionierte, war zu bestehen getActivity(); dh

progressDialog = new ProgressDialog(getActivity());


6

Verwenden MyDialog md = new MyDialog(MyActivity.this.getParent());


6

Wenn Sie sich außerhalb der Aktivität befinden, müssen Sie in Ihrer Funktion "NameOfMyActivity.this" als Aktivitätsaktivität verwenden. Beispiel:

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

Wenn Sie ein Fragment und eine AlertDialog / ToastNachricht verwenden, verwenden SiegetActivity() den Kontextparameter.

Hat für mich gearbeitet.

Prost!


5

Versuchen Sie, den Kontext einer Aktivität zu verwenden, die sich unter dem Dialog befindet. Seien Sie jedoch vorsichtig, wenn Sie das Schlüsselwort "this" verwenden, da es nicht jedes Mal funktioniert.

Wenn Sie beispielsweise TabActivity als Host mit zwei Registerkarten haben und jede Registerkarte eine andere Aktivität ist und wenn Sie versuchen, einen Dialog aus einer der Registerkarten (Aktivitäten) zu erstellen, und wenn Sie "dies" verwenden, erhalten Sie eine Ausnahme Der Falldialog sollte mit der Hostaktivität verbunden sein, die alles hostet und sichtbar ist. (Sie können den Kontext der sichtbarsten übergeordneten Aktivität sagen)

Ich habe diese Informationen in keinem Dokument gefunden, sondern nur durch einen Versuch. Dies ist meine Lösung ohne starken Hintergrund. Wenn jemand mit besserem Wissen, zögern Sie nicht zu kommentieren.


4

Für zukünftige Leser sollte dies helfen:

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


2

Oder Sie können den Dialog wie folgt erstellen:

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

2

Ich denke, es kann auch passieren, wenn Sie versuchen, einen Dialog aus einem Thread anzuzeigen, der nicht der Haupt-UI-Thread ist.

Verwenden runOnUiThread() Sie in diesem Fall.


2

Versuchen Sie es getParent()am Argumentationsort des Kontextes wie neu. AlertDialog.Builder(getParent());Hoffe, es wird funktionieren, es hat bei mir funktioniert.


1

Nachdem Sie sich die API angesehen haben, können Sie dem Dialogfeld Ihre Aktivität oder getActivity übergeben, wenn Sie sich in einem Fragment befinden, und es dann mit dialog.dismiss () in den Rückgabemethoden zwangsweise bereinigen, um Lecks zu vermeiden.

Obwohl es nirgendwo explizit angegeben ist, wo ich weiß, scheint es, dass Sie den Dialog in den OnClickHandlers zurückgegeben haben, nur um dies zu tun.


0

Wenn Ihr Dialog auf dem Adapter erstellt wird:

Übergeben Sie die Aktivität an den Adapterkonstruktor:

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

Auf dem Adapter empfangen:

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

Jetzt können Sie auf Ihrem Builder verwenden

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

-1

So habe ich denselben Fehler für meine Anwendung behoben:
Hinzufügen der folgenden Zeile nach dem Erstellen des Dialogfelds:

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

Sie müssen keinen Kontext erwerben. Dies ist besonders nützlich, wenn Sie ein anderes Dialogfeld über dem aktuell angezeigten Dialogfeld öffnen. Oder wenn es nicht bequem ist, einen Kontext zu erhalten.

Ich hoffe, dies kann Ihnen bei Ihrer App-Entwicklung helfen.

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();
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.