Boîte de dialogue lançant «Impossible d'ajouter une fenêtre - le jeton nul n'est pas pour une application» avec getApplication () comme contexte


665

Mon activité tente de créer un AlertDialog qui nécessite un contexte comme paramètre. Cela fonctionne comme prévu si j'utilise:

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

Cependant, je me méfie d'utiliser "this" comme contexte en raison du potentiel de fuites de mémoire lorsque Activity est détruit et recréé même pendant quelque chose de simple comme une rotation d'écran. À partir d'un article connexe sur le blog du développeur Android :

Il existe deux moyens simples d'éviter les fuites de mémoire liées au contexte. La plus évidente est d'éviter d'échapper au contexte en dehors de sa propre portée. L'exemple ci-dessus montre le cas d'une référence statique mais les classes internes et leur référence implicite à la classe externe peuvent être tout aussi dangereuses. La deuxième solution consiste à utiliser le contexte d'application. Ce contexte vivra tant que votre application sera en vie et ne dépendra pas du cycle de vie des activités. Si vous prévoyez de conserver des objets à longue durée de vie qui ont besoin d'un contexte, n'oubliez pas l'objet d'application. Vous pouvez l'obtenir facilement en appelant Context.getApplicationContext () ou Activity.getApplication ().

Mais pour le AlertDialog()ni getApplicationContext()ou getApplication()est acceptable comme contexte, car il lève l'exception:

"Impossible d'ajouter une fenêtre - le token null n'est pas pour une application"

par références: 1 , 2 , 3 , etc.

Donc, cela devrait-il vraiment être considéré comme un "bug", car il est officiellement conseillé de l'utiliser Activity.getApplication()et pourtant il ne fonctionne pas comme annoncé?

Jim


référence pour le premier article où R.Guy conseille d'utiliser getApplication: android-developers.blogspot.com/2009/01/…
gymshoe




Réponses:


1354

Au lieu de getApplicationContext(), utilisez simplement ActivityName.this.


67
Génial! Juste pour faire un commentaire à ce sujet .. vous pouvez parfois avoir besoin de stocker "ceci" globalement (par exemple) afin d'y accéder dans la méthode implémentée d'un auditeur qui a son propre "ceci". Dans ce cas, vous définiriez «Context context» globalement, puis dans onCreate, définir «context = this», puis vous référer à «context». J'espère que cela vous sera utile aussi.
Steven L

8
En fait, comme les Listenercours sont souvent anonymes, j'ai tendance à le faire final Context ctx = this;et je suis absent;)
Alex

28
@StevenL Pour faire ce que vous dites, vous devez utiliser ExternalClassName.this pour faire explicitement référence à "this" de la classe externe.
Artem Russakovskii

11
Est-ce que l'utilisation de "this" ne le ferait pas fuir si votre boîte de dialogue est utilisée dans un rappel et que vous quittez l'activité avant l'appel du rappel? C'est du moins ce dont Android semble se plaindre dans logcat.
Artem Russakovskii

6
Je ne conseillerais pas l'approche de @StevenLs car vous pouvez facilement fuir la mémoire de cette activité à moins que vous vous souveniez d'effacer la référence statique dans onDestroy - Artem est correct. L'approche de Steven est due au manque de compréhension du fonctionnement de Java
Dori

192

L'utilisation thisn'a pas fonctionné pour moi, mais l'a MyActivityName.thisfait. J'espère que cela aide toute personne qui n'a pas pu se rendre thisau travail.


63
C'est ce qui se produit lorsque vous utilisez thisde l'intérieur d'une classe interne. Si vous souhaitez référencer l'instance d'une classe externe, vous devez le spécifier, comme vous le faites avec OuterClass.this. Utiliser simplement fait thistoujours référence à l'instance la plus interne de la classe.
kaka

60

Vous pouvez continuer à utiliser getApplicationContext(), mais avant utilisation, vous devez ajouter cet indicateur:, dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)et l'erreur ne s'affichera pas.

Ajoutez l'autorisation suivante à votre manifeste:

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

1
Je ne parviens pas à ajouter la fenêtre android.view.ViewRootImpl$W@426ce670 - autorisation refusée pour ce type de fenêtre
Ram G.

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

3
Il semble que vous ne puissiez pas activer cette autorisation dans l'API 23 à partir de code.google.com/p/android-developer-preview/issues/…
roy zhang

1
Vous pouvez l'utiliser pour l'API 23, mais vous devez inviter l'utilisateur: startActivityForResult (new Intent (Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse ("package:" + getPackageName ())), OVERLAY_PERMISSION_REQ_CODE); Toutefois, si vous devez l' utiliser est une autre affaire ...
Ben Neill

2
Ceci est utile lorsque vous affichez une boîte de dialogue de progression dans le service
Anand Savjani

37

Vous avez correctement identifié le problème lorsque vous avez dit "... pour AlertDialog () ni getApplicationContext () ni getApplication () n'est acceptable en tant que contexte, car il lève l'exception:" Impossible d'ajouter une fenêtre - le token null n'est pas pour une application'"

Pour créer une boîte de dialogue, vous avez besoin d'un contexte d'activité ou d'un contexte de service , pas d'un contexte d'application (les deux getApplicationContext () et getApplication () renvoient un contexte d'application).

Voici comment vous obtenez le contexte d'activité :

(1) Dans une activité ou un service:

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

(2) Dans un fragment: AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

Les fuites de mémoire ne sont pas un problème intrinsèque à la référence "this", qui est la référence d'un objet à lui-même (c'est-à-dire la référence à la mémoire réellement allouée pour stocker les données de l'objet). Cela arrive à toute mémoire allouée pour laquelle le garbage collector (GC) est incapable de libérer une fois que la mémoire allouée a dépassé sa durée de vie utile.

La plupart du temps, lorsqu'une variable sort de la portée, la mémoire est récupérée par le GC. Cependant, des fuites de mémoire peuvent se produire lorsque la référence à un objet détenu par une variable, par exemple "x", persiste même après que l'objet a survécu à sa durée de vie utile. La mémoire allouée sera donc perdue aussi longtemps que "x" y fera référence car GC ne libérera pas la mémoire tant que cette mémoire sera toujours référencée. Parfois, les fuites de mémoire ne sont pas apparentes en raison d' une chaîne de références à la mémoire allouée. Dans un tel cas, le GC ne libérera pas la mémoire tant que toutes les références à cette mémoire n'auront pas été supprimées.

Pour éviter les fuites de mémoire, recherchez dans votre code des erreurs logiques qui provoquent un référencement indéfini de la mémoire allouée par «ceci» (ou d'autres références). N'oubliez pas de vérifier également les références de chaîne. Voici quelques outils que vous pouvez utiliser pour vous aider à analyser l'utilisation de la mémoire et à trouver ces fuites de mémoire embêtantes:


Pour une activité, vous pouvez également utiliser ActivityName.thisActivityName est (évidemment) le nom de votre activité (par exemple MainActivity)
Luis Cabrera Benito

34

Votre dialogue ne doit pas être un "objet à longue durée de vie qui a besoin d'un contexte". La documentation est déroutante. Fondamentalement, si vous faites quelque chose comme:

static Dialog sDialog;

(notez le statique )

Ensuite, dans une activité quelque part que vous avez fait

 sDialog = new Dialog(this);

Vous feriez probablement fuir l'activité d'origine lors d'une rotation ou similaire qui détruirait l'activité. (À moins que vous ne nettoyiez dans onDestroy, mais dans ce cas, vous ne rendriez probablement pas l'objet Dialog statique)

Pour certaines structures de données, il serait logique de les rendre statiques et basées sur le contexte de l'application, mais généralement pas pour les choses liées à l'interface utilisateur, comme les boîtes de dialogue. Donc quelque chose comme ça:

Dialog mDialog;

...

mDialog = new Dialog(this);

Est bien et ne devrait pas fuir l'activité car mDialog serait libéré avec l'activité car elle n'est pas statique.


Je l'appelle depuis un asynctask, cela a fonctionné pour moi, thx mate
MemLeak

ma boîte de dialogue était statique, une fois que j'ai supprimé la déclaration statique, cela a fonctionné.
Ceddy Muhoza

25

J'ai dû envoyer mon contexte via un constructeur sur un adaptateur personnalisé affiché dans un fragment et j'ai eu ce problème avec getApplicationContext (). Je l'ai résolu avec:

this.getActivity().getWindow().getContext()dans le onCreaterappel des fragments .


4
Cela a fonctionné pour moi aussi, je l'ai transmis au constructeur de AsyncTask externe que j'utilise (il affiche une boîte de dialogue de progression).
Rohan Kandwal

3
c'est la VRAIE réponse pour des tâches plus complexes :)
teejay

1
Je suis d'accord avec @teejay
Erdi İzgi

23

dans l' activité, utilisez simplement:

MyActivity.this

en fragment:

getActivity();

Cela m'a corrigé dans mon activité. Merci
Werner

20

En Activitycliquant sur le bouton affichant une boîte de dialogue

Dialog dialog = new Dialog(MyActivity.this);

A travaillé pour moi.


19

***** version kotlin *****

Vous devez passer this@YourActivityau lieu de applicationContextoubaseContext


18

Petit hack: vous pouvez empêcher de détruire votre activité par GC (vous ne devriez pas le faire, mais il peut aider dans certaines situations Ne pas oublier de mettre. contextForDialogÀ nullquand il est plus nécessaire):

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 Cela fonctionne parce que cette == PostActivity qui hérite de Activity-> qui hérite de Context, donc lorsque vous passez la boîte de dialogue votre contexte, vous passez réellement l'activité
Elad Gelman

13

Si vous utilisez un fragment et utilisez le message AlertDialog / Toast, utilisez getActivity () dans le paramètre context.

comme ça

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

12

Utilisez simplement ce qui suit:

POUR LES UTILISATEURS DE JAVA

Si vous utilisez l'activité -> AlertDialog.Builder builder = new AlertDialog.Builder(this);

OU

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

Si vous utilisez un fragment -> AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

POUR LES UTILISATEURS DE KOTLIN

Si vous utilisez l'activité -> val builder = AlertDialog.Builder(this)

OU

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

Si vous utilisez un fragment -> val builder = AlertDialog.Builder(activity!!)


9

ajouter

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

et

"android.permission.SYSTEM_ALERT_WINDOW"/> en manifeste

Ça marche pour moi maintenant. Après même fermer et ouvrir l'application, m'a donné l'erreur à ce moment-là.


9

J'utilisais ProgressDialogdans un fragment et obtenais cette erreur en passant getActivity().getApplicationContext()comme paramètre constructeur. Le changer en getActivity().getBaseContext()n'a pas fonctionné non plus.

La solution qui a fonctionné pour moi était de passer getActivity(); c'est à dire

progressDialog = new ProgressDialog(getActivity());


6

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


6

Si vous êtes en dehors de l'activité, vous devez utiliser dans votre fonction "NameOfMyActivity.this" comme activité d'activité, par exemple:

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

Si vous utilisez un fragment et utilisez un AlertDialog / Toastmessage, utilisez-le getActivity()dans le paramètre context.

A travaillé pour moi.

À votre santé!


5

Essayez d'utiliser le contexte d'une activité qui sera sous la boîte de dialogue. Mais soyez prudent lorsque vous utilisez "ce" mot-clé, car il ne fonctionnera pas à chaque fois.

Par exemple, si vous avez TabActivity comme hôte avec deux onglets, et chaque onglet est une autre activité, et si vous essayez de créer une boîte de dialogue à partir de l'un des onglets (activités) et si vous utilisez "ceci", vous obtiendrez une exception, dans ce la boîte de dialogue de cas doit être connectée à l'activité hôte qui héberge tout et visible. (vous pouvez dire le contexte de l'activité parent la plus visible)

Je n'ai trouvé cette information dans aucun document mais en essayant. Ceci est ma solution sans fond solide, Si quelqu'un avec une meilleure connaissance, n'hésitez pas à commenter.


4

Pour les futurs lecteurs, cela devrait aider:

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


2

Ou une autre possibilité est de créer une boîte de dialogue comme suit:

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

2

Je pense que cela peut se produire également si vous essayez d'afficher une boîte de dialogue à partir d'un thread qui n'est pas le thread principal de l'interface utilisateur.

Utilisez runOnUiThread()dans ce cas.


2

Essayez getParent()à l'argument lieu de contexte comme new AlertDialog.Builder(getParent());Hope ça marchera, ça a marché pour moi.


1

Après avoir jeté un œil à l'API, vous pouvez passer la boîte de dialogue de votre activité ou getActivity si vous êtes dans un fragment, puis le nettoyer avec force avec dialog.dismiss () dans les méthodes de retour pour éviter les fuites.

Bien que cela ne soit pas explicitement indiqué partout où je sais, il semble que vous ayez renvoyé la boîte de dialogue dans les OnClickHandlers juste pour le faire.


0

Si votre boîte de dialogue crée sur l'adaptateur:

Passez l'activité au constructeur de l'adaptateur:

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

Recevez sur l'adaptateur:

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

Vous pouvez maintenant l'utiliser sur votre Builder

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

-1

Voici comment j'ai résolu la même erreur pour mon application:
Ajout de la ligne suivante après avoir créé la boîte de dialogue:

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

Vous n'aurez pas besoin d'acquérir un contexte. Cela est particulièrement utile si vous ouvrez une autre boîte de dialogue par-dessus la boîte de dialogue contextuelle actuelle. Ou quand il n'est pas pratique d'obtenir un contexte.

J'espère que cela peut vous aider avec le développement de votre application.

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();
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.