Finestra di dialogo che lancia "Impossibile aggiungere la finestra - token null non è per un'applicazione" con getApplication () come contesto


665

La mia attività sta provando a creare un AlertDialog che richiede un contesto come parametro. Funziona come previsto se uso:

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

Tuttavia, sono diffidente nell'usare "questo" come contesto a causa del potenziale per perdite di memoria quando l'attività viene distrutta e ricreata anche durante qualcosa di semplice come una rotazione dello schermo. Da un post correlato sul blog dello sviluppatore Android :

Esistono due semplici modi per evitare perdite di memoria relative al contesto. Il più ovvio è evitare di sfuggire al contesto al di fuori del proprio ambito. L'esempio sopra ha mostrato il caso di un riferimento statico ma le classi interne e il loro riferimento implicito alla classe esterna possono essere ugualmente pericolosi. La seconda soluzione è utilizzare il contesto dell'applicazione. Questo contesto durerà fino a quando l'applicazione sarà attiva e non dipenderà dal ciclo di vita delle attività. Se si prevede di mantenere oggetti di lunga durata che richiedono un contesto, ricordare l'oggetto dell'applicazione. Puoi ottenerlo facilmente chiamando Context.getApplicationContext () o Activity.getApplication ().

Ma per AlertDialog()nessuno dei due getApplicationContext()o getApplication()è accettabile come contesto, in quanto genera l'eccezione:

"Impossibile aggiungere la finestra - token null non è per un'applicazione"

per riferimenti: 1 , 2 , 3 , ecc.

Quindi, questo dovrebbe davvero essere considerato un "bug", dal momento che ci viene ufficialmente consigliato di usare Activity.getApplication()e tuttavia non funziona come pubblicizzato?

Jim


riferimento per il primo articolo in cui R.Guy consiglia di utilizzare getApplication: android-developers.blogspot.com/2009/01/…
gymshoe




Risposte:


1354

Invece di getApplicationContext(), basta usare ActivityName.this.


67
Grande! Solo per commentare questo .. a volte potrebbe essere necessario archiviare "questo" a livello globale, (ad esempio) per accedervi all'interno del metodo implementato da un ascoltatore che ha il proprio "questo". In tal caso, dovresti definire "Contesto del contesto" a livello globale, quindi in onCreate, impostare "contesto = questo", quindi fare riferimento a "contesto". Spero che sia utile anche a te.
Steven L

8
In realtà, dato che le Listenerlezioni sono spesso anonime-interne, tendo solo a farlo final Context ctx = this;e sono via;)
Alex

28
@StevenL Per fare ciò che stai dicendo, dovresti usare ExternalClassName.this per riferirti esplicitamente a "questo" della classe esterna.
Artem Russakovskii,

11
Usando "questo" non lo perderebbe se la tua finestra di dialogo viene utilizzata in un callback e lasci l'attività prima che il callback venga chiamato? Almeno questo è ciò di cui sembra lamentarsi Android in Logcat.
Artem Russakovskii,

6
Non consiglierei l'approccio di @StevenLs in quanto puoi facilmente perdere la memoria di quell'attività a meno che non ti ricordi di cancellare il riferimento statico in onDestroy - Artem è corretto. L'approccio di StevenLe nasce dalla mancanza di comprensione di come funziona Java
Dori,

192

L'uso thisnon ha funzionato per me, ma MyActivityName.thisha funzionato. Spero che questo aiuti chiunque non possa andare thisal lavoro.


63
Questo è ciò che accade quando si utilizza thisdall'interno di una classe interna. Se si desidera fare riferimento all'istanza di una classe esterna, è necessario specificarlo, come si fa con OuterClass.this. Il solo uso fa thissempre riferimento all'istanza della classe più interna.
Kaka,

60

È possibile continuare a utilizzare getApplicationContext(), ma prima dell'uso, è necessario aggiungere questo flag: dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)e l'errore non verrà visualizzato.

Aggiungi la seguente autorizzazione al tuo manifest:

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

1
Ottengo impossibile aggiungere la finestra android.view.ViewRootImpl$W@426ce670 - autorizzazione negata per questo tipo di finestra
Ram G.

aggiungi permesso: <usi-permesso android: nome = "android.permission.SYSTEM_ALERT_WINDOW" />
codezjx

3
Sembra che tu non possa abilitare questa autorizzazione nell'API 23 in poi code.google.com/p/android-developer-preview/issues/…
roy zhang,

1
È possibile utilizzarlo per l'API 23 in poi, tuttavia è necessario richiedere all'utente: startActivityForResult (nuovo intento (Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse ("pacchetto:" + getPackageName ())), OVERLAY_PERMISSION_REQ_CODE); tuttavia, se dovresti usarlo è un'altra cosa ...
Ben Neill,

2
Ciò è utile quando si mostra una finestra di dialogo sull'avanzamento all'interno del servizio
Anand Savjani,

37

Hai identificato correttamente il problema quando hai detto "... per AlertDialog () né getApplicationContext () o getApplication () è accettabile come contesto, poiché genera l'eccezione:" Impossibile aggiungere la finestra - token null non è per un applicazione'"

Per creare una finestra di dialogo, è necessario un contesto di attività o un contesto di servizio , non un contesto di applicazione (sia getApplicationContext () che getApplication () restituiscono un contesto di applicazione).

Ecco come ottenere il contesto di attività :

(1) In un'attività o un servizio:

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

(2) In un frammento: AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

Le perdite di memoria non sono un problema intrinseco al riferimento "this", che è il riferimento di un oggetto a se stesso (ovvero riferimento alla memoria allocata effettiva per la memorizzazione dei dati dell'oggetto). Accade a qualsiasi memoria allocata per la quale Garbage Collector (GC) non è in grado di liberarsi dopo che la memoria allocata ha superato la sua durata utile.

Il più delle volte, quando una variabile esce dal campo di applicazione, la memoria verrà recuperata dal GC. Tuttavia, possono verificarsi perdite di memoria quando il riferimento a un oggetto contenuto da una variabile, ad esempio "x", persiste anche dopo che l'oggetto ha superato la sua durata utile. La memoria allocata andrà quindi persa fintanto che "x" contiene un riferimento ad essa perché GC non libererà la memoria fintanto che quella memoria sarà ancora referenziata. A volte, le perdite di memoria non sono evidenti a causa di una catena di riferimenti alla memoria allocata. In tal caso, il GC non libererà la memoria fino a quando tutti i riferimenti a quella memoria non saranno stati rimossi.

Per evitare perdite di memoria, controllare il codice per errori logici che causano un riferimento indefinito alla memoria allocata da "this" (o altri riferimenti). Ricorda di controllare anche i riferimenti a catena. Ecco alcuni strumenti che puoi utilizzare per aiutarti ad analizzare l'uso della memoria e trovare quelle fastidiose perdite di memoria:


Per un'attività è anche possibile utilizzare ActivityName.this dove activityName è (ovviamente) il nome del vostro attività (ad esempio MainActivity)
Luis Cabrera Benito

34

Il tuo dialogo non dovrebbe essere un "oggetto di lunga durata che necessita di un contesto". La documentazione è confusa. Fondamentalmente se fai qualcosa del tipo:

static Dialog sDialog;

(nota la statica )

Quindi in un'attività da qualche parte hai fatto

 sDialog = new Dialog(this);

Probabilmente perderai l'attività originale durante una rotazione o simile che distruggerebbe l'attività. (A meno che non si pulisca in onDestroy, ma in quel caso probabilmente non si renderebbe l'oggetto Dialog statico)

Per alcune strutture di dati avrebbe senso renderle statiche e basate sul contesto dell'applicazione, ma generalmente non per cose relative all'interfaccia utente, come le finestre di dialogo. Quindi qualcosa del genere:

Dialog mDialog;

...

mDialog = new Dialog(this);

Va bene e non dovrebbe perdere l'attività poiché mDialog verrebbe liberato dall'attività poiché non è statico.


lo chiamo da un asincrono, questo ha funzionato per me, grazie
amico

la mia finestra di dialogo era statica, una volta rimossa la dichiarazione statica funzionava.
Ceddy Muhoza,

25

Ho dovuto inviare il mio contesto tramite un costruttore su un adattatore personalizzato visualizzato in un frammento e ho riscontrato questo problema con getApplicationContext (). L'ho risolto con:

this.getActivity().getWindow().getContext()nel onCreatecallback dei frammenti .


4
Questo ha funzionato anche per me, l'ho passato al costruttore di AsyncTask esterno che sto usando (mostra una finestra di avanzamento).
Rohan Kandwal,

3
questa è la vera risposta per compiti più complessi :)
teejay,

1
Concordo con @teejay
Erdi İzgi il

23

in Attività basta usare:

MyActivity.this

nel frammento:

getActivity();

Questo mi ha risolto nella mia attività. Grazie
Werner il

20

Nel Activityclic del pulsante che mostra una finestra di dialogo

Dialog dialog = new Dialog(MyActivity.this);

Ha funzionato per me.


19

***** versione kotlin *****

Dovresti passare this@YourActivityinvece di applicationContextobaseContext


18

Piccolo hack: si può impedire di distruggere la vostra attività da GC (non si dovrebbe fare, ma può aiutare in alcune situazioni Non dimenticare di set. contextForDialogA nullquando non è più avuto bisogno):

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 Funziona perché questo == PostActivity che eredita da Activity-> che eredita da Context, quindi quando passi la finestra di dialogo il tuo contesto in realtà stai passando l'attività
Elad Gelman

13

Se si utilizza un frammento e si utilizza il messaggio AlertDialog / Toast, utilizzare getActivity () nel parametro di contesto.

come questo

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

12

Basta usare quanto segue:

PER GLI UTENTI JAVA

Nel caso in cui si utilizzi attività -> AlertDialog.Builder builder = new AlertDialog.Builder(this);

O

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

Nel caso in cui stai usando il frammento -> AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

PER GLI UTENTI KOTLIN

Nel caso in cui si utilizzi attività -> val builder = AlertDialog.Builder(this)

O

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

Nel caso in cui stai usando il frammento -> val builder = AlertDialog.Builder(activity!!)


9

aggiungendo

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

e

"android.permission.SYSTEM_ALERT_WINDOW"/> in manifest

Funziona per me adesso. Dopo aver persino chiuso e aperto l'applicazione, mi ha dato l'errore in quel momento.


9

Stavo usando ProgressDialogin un frammento e stavo ottenendo questo errore passando getActivity().getApplicationContext()come parametro del costruttore. Anche cambiarlo in getActivity().getBaseContext()non ha funzionato.

La soluzione che ha funzionato per me era passare getActivity(); vale a dire

progressDialog = new ProgressDialog(getActivity());


6

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


6

Se sei al di fuori dell'attività, devi utilizzare la funzione "NameOfMyActivity.this" come attività dell'attività, ad esempio:

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

Se si utilizza un frammento e si utilizza un AlertDialog / Toastmessaggio, utilizzare getActivity()nel parametro di contesto.

Ha funzionato per me.

Saluti!


5

Prova a usare il contesto di un'attività che sarà sotto la finestra di dialogo. Ma fai attenzione quando usi "questa" parola chiave, perché non funzionerà ogni volta.

Ad esempio, se hai TabActivity come host con due schede e ogni scheda è un'altra attività, e se provi a creare una finestra di dialogo da una delle schede (attività) e se usi "questo", otterrai un'eccezione, In questo la finestra di dialogo case dovrebbe essere connessa all'attività host che ospita tutto e visibile. (puoi dire il contesto dell'attività genitore più visibile)

Non ho trovato queste informazioni da nessun documento ma provandoci. Questa è la mia soluzione senza un forte background, se qualcuno con una conoscenza migliore, sentiti libero di commentare.


4

Per i futuri lettori, questo dovrebbe aiutare:

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


2

Oppure un'altra possibilità è quella di creare la finestra di dialogo come segue:

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

2

Penso che possa accadere anche se stai cercando di mostrare una finestra di dialogo da un thread che non è il thread dell'interfaccia utente principale.

Utilizzare runOnUiThread()in tal caso.


2

Prova getParent()al luogo argomento del contesto come nuovo AlertDialog.Builder(getParent());Spero che funzionerà, ha funzionato per me.


1

Dopo aver dato un'occhiata all'API, puoi passare la finestra di dialogo alla tua attività o getActivity se sei in un frammento, quindi ripulirla con forza con dialog.dismiss () nei metodi di restituzione per evitare perdite.

Sebbene non sia esplicitamente dichiarato ovunque io sappia, sembra che tu sia passato indietro alla finestra di dialogo in OnClickHandlers solo per fare questo.


0

Se la finestra di dialogo viene creata sull'adattatore:

Passa l'attività al costruttore dell'adattatore:

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

Ricevi sull'adattatore:

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

Ora puoi usare il tuo Builder

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

-1

Ecco come ho risolto lo stesso errore per la mia applicazione:
aggiungendo la seguente riga dopo aver creato la finestra di dialogo:

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

Non sarà necessario acquisire un contesto. Ciò è particolarmente utile se si sta spuntando un'altra finestra di dialogo sulla finestra di dialogo corrente. O quando non è conveniente ottenere un contesto.

Spero che questo possa aiutarti nello sviluppo della tua app.

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();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.