"Android.view.WindowManager $ BadTokenException: Impossible d'ajouter la fenêtre" sur buider.show ()


120

De mon côté activity, j'ai besoin d'appeler une classe interne et dans une méthode au sein de la classe, j'ai besoin de montrer AlertDialog. Après l'avoir rejeté, lorsque vous appuyez sur le bouton OK, passez à Google Play pour achat.

Les choses fonctionnent parfaitement pour la plupart du temps, mais pour quelques utilisateurs, il plante builder.show()et je peux voir "android.view.WindowManager$BadTokenException:Impossible d'ajouter la fenêtre "dans le journal des pannes. Veuillez suggérer.

Mon code est à peu près comme ceci:

public class classname1 extends Activity{

  public void onCreate(Bundle savedInstanceState) {
    this.requestWindowFeature(Window.FEATURE_NO_TITLE);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.<view>); 

    //call the <className1> class to execute
  }

  private class classNamename2 extends AsyncTask<String, Void, String>{

    protected String doInBackground(String... params) {}

    protected void onPostExecute(String result){
      if(page.contains("error")) 
      {
        AlertDialog.Builder builder = new AlertDialog.Builder(classname1.this);
        builder.setCancelable(true);
        builder.setMessage("");
        builder.setInverseBackgroundForced(true);
        builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
          public void onClick(DialogInterface dialog, int whichButton){
            dialog.dismiss();
            if(!<condition>)
            {
              try
              {
                String pl = ""; 

                mHelper.<flow>(<class>.this, SKU, RC_REQUEST, 
                  <listener>, pl);
              }

              catch(Exception e)
              {
                e.printStackTrace();
              }
            }  
          }
        });

        builder.show();
      }
    }
  }
}

J'ai également vu l'erreur dans une autre alerte où je ne transmets à aucune autre activity. C'est simple comme ça:

AlertDialog.Builder builder = new AlertDialog.Builder(classname1.this);
    builder.setCancelable(true);

    //if successful
    builder.setMessage(" ");
    builder.setInverseBackgroundForced(true);
    builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int whichButton){
            // dialog.dismiss();
                   }
    });
    builder.show();
}

2
S'il s'agit de votre code complet, avez-vous vraiment besoin d'AsyncTask?
Shobhit Puri

Ce n'est pas le code complet, c'est un assez gros code donc j'ai seulement ajouté la partie ici où je vois un problème de rapport de plantage
MSIslam

d'accord, bien. Habituellement, vous pouvez simplement publier le nom de la fonction et commenter que vous y faites beaucoup de choses (comme vous l'avez fait maintenant). C'est plus facile à comprendre. :).
Shobhit Puri

Naviguez-vous vers une autre activité à partir de cette activité quelque part?
Shobhit Puri

1
Vous avez écrit des commentaires //send to some other activity. Je pense que si vous commentez la partie où vous allez à une nouvelle activité, cette erreur disparaîtra. L'erreur semble se produire car votre boîte de dialogue avant est complètement fermée, votre nouvelle activité démarre. Dans le onPostExecute(), vous avez la boîte de dialogue d'alerte et vous donnez le contexte de l' loginactivité. Mais vous naviguez vers l'autre activité, le contexte devient donc erroné. Par conséquent, vous obtenez cette erreur! Voir stackoverflow.com/questions/15104677/... question similaire.
Shobhit Puri

Réponses:


266
android.view.WindowManager$BadTokenException: Unable to add window"

Problème:

Cette exception se produit lorsque l'application tente d'avertir l'utilisateur à partir du thread d'arrière-plan (AsyncTask) en ouvrant une boîte de dialogue.

Vous obtenez cette erreur.

Raison:

La raison de cette exception est que, comme l'indique le message d'exception, l'activité est terminée mais vous essayez d'afficher une boîte de dialogue avec un contexte de l'activité terminée. Puisqu'il n'y a pas de fenêtre pour que la boîte de dialogue affiche le runtime Android, cette exception est levée.

Solution:

Utilisez la isFinishing()méthode appelée par Android pour vérifier si cette activité est en train de se terminer: qu'il s'agisse d'un appel de fin () explicite ou d'un nettoyage d'activité effectué par Android. En utilisant cette méthode, il est très facile d'éviter d'ouvrir la boîte de dialogue à partir du thread d'arrière-plan lorsque l'activité se termine.

Conservez également un weak referencepour l'activité (et non une référence forte pour que l'activité puisse être détruite une fois inutile) et vérifiez si l'activité ne se termine pas avant d'exécuter une interface utilisateur en utilisant cette référence d'activité (c'est-à-dire en affichant une boîte de dialogue).

par exemple .

private class chkSubscription extends AsyncTask<String, Void, String>{

  private final WeakReference<login> loginActivityWeakRef;

  public chkSubscription (login loginActivity) {
    super();
    this.loginActivityWeakRef= new WeakReference<login >(loginActivity)
  }

  protected String doInBackground(String... params) {
    //web service call
  }

  protected void onPostExecute(String result) {
    if(page.contains("error")) //when not subscribed
    {
      if (loginActivityWeakRef.get() != null && !loginActivityWeakRef.get().isFinishing()) {
        AlertDialog.Builder builder = new AlertDialog.Builder(login.this);
        builder.setCancelable(true);
        builder.setMessage(sucObject);
        builder.setInverseBackgroundForced(true);

        builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
          public void onClick(DialogInterface dialog, int whichButton){
            dialog.dismiss();
          }
        });

        builder.show();
      }
    }
  }
}

Mettre à jour :

Jetons de fenêtre:

Comme son nom l'indique, un jeton de fenêtre est un type spécial de jeton de classeur que le gestionnaire de fenêtres utilise pour identifier de manière unique une fenêtre dans le système. Les jetons de fenêtre sont importants pour la sécurité car ils empêchent les applications malveillantes de s'afficher au-dessus des fenêtres d'autres applications. Le gestionnaire de fenêtres se protège contre cela en demandant aux applications de transmettre le jeton de fenêtre de leur application dans le cadre de chaque demande d'ajout ou de suppression d'une fenêtre. Si les jetons ne correspondent pas, le gestionnaire de fenêtres rejette la demande et lève une BadTokenException . Sans les jetons de fenêtre, cette étape d'identification nécessaire ne serait pas possible et le gestionnaire de fenêtres ne pourrait pas se protéger des applications malveillantes.

 Un scénario réel:

Lorsqu'une application démarre pour la première fois,  ActivityManagerService  crée un type spécial de jeton de fenêtre appelé jeton de fenêtre d'application, qui identifie de manière unique la fenêtre de conteneur de niveau supérieur de l'application. Le gestionnaire d'activités donne ce jeton à la fois à l'application et au gestionnaire de fenêtres, et l'application envoie le jeton au gestionnaire de fenêtres chaque fois qu'elle souhaite ajouter une nouvelle fenêtre à l'écran. Cela garantit une interaction sécurisée entre l'application et le gestionnaire de fenêtres (en rendant impossible l'ajout de fenêtres par-dessus d'autres applications), et permet également au gestionnaire d'activités de faire des demandes directes au gestionnaire de fenêtres.


Cela a beaucoup de sens! Votre solution me semble également excellente. (y)
MSIslam

'Le champ final vide loginActivityWeakRef n'a peut-être pas été initialisé' et a essayé de cette manière: private final WeakReference <login> loginActivityWeakRef = new WeakReference <login> (login.this); Je ne sais pas si c'est la bonne chose à faire
MSIslam

J'ai également supprimé le dernier avant WeakReference <login> loginActivityWeakRef car il montrait une erreur dans le constructeur.
MSIslam

1
essayez d'utiliser le nouveau chkCubscription (this) .execute (""); au lieu de new chkCubscription.execute (""); comme vous l'avez posté ci-dessus.
Ritesh Gune

2
Erreur terrible !! Je suis un tutoriel et en tant que @PhilRoggenbuck, mon problème a été causé par l'appel d'un Toast..Show () juste avant d'appeler StartActivity (...). Pour résoudre ce problème, j'ai déplacé le pain grillé dans l'activité nouvellement appelée à la place!
Thierry

27

J'ai eu un dialogue montrant la fonction:

void showDialog(){
    new AlertDialog.Builder(MyActivity.this)
    ...
    .show();
}

J'obtenais cette erreur et je devais juste vérifier isFinishing()avant d'appeler cette boîte de dialogue affichant la fonction.

if(!isFinishing())
    showDialog();

1
ne devrions-nous pas écrire if(!MyActivity.this.isFinishing())? Si ce n'est pas bien dans MyActivity
Bibaswann Bandyopadhyay

2
Pourquoi Android exécuterait-il du code s'il se termine déjà? Si nous suivons cette solution, imaginez combien de fois nous devrions vraiment utiliser isFinishing pour éviter des problèmes similaires.
David

@David Je pense qu'il manque quelques détails, comme la boîte de dialogue appelée dans un fil d'arrière-plan, mais je suis complètement d'accord avec votre point tel qu'il est maintenant.
Panier abandonné le

Bon point, pourquoi diable aurais-je besoin de vérifier isFinishing!
Chibueze Opata

9

La raison possible est le contexte de la boîte de dialogue d'alerte. Vous avez peut-être terminé cette activité, c'est pourquoi vous essayez d'ouvrir dans ce contexte mais qui est déjà fermé. Essayez de changer le contexte de cette boîte de dialogue pour votre première activité car elle ne sera pas terminée avant la fin.

par exemple

plutôt que ça.

AlertDialog alertDialog = new AlertDialog.Builder(this).create();

Essayez d'utiliser

AlertDialog alertDialog = new AlertDialog.Builder(FirstActivity.getInstance()).create();

3
  • d'abord, vous ne pouvez pas étendre AsyncTask sans remplacer doInBackground
  • essayez ensuite de créer AlterDailog à partir du générateur, puis appelez show ().

    private boolean visible = false;
    class chkSubscription extends AsyncTask<String, Void, String>
    {
    
        protected void onPostExecute(String result)
        {
            AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
            builder.setCancelable(true);
            builder.setMessage(sucObject);
            builder.setInverseBackgroundForced(true);
            builder.setNeutralButton("Ok", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton)
                {
                    dialog.dismiss();
                }
            });
    
            AlertDialog myAlertDialog = builder.create();
            if(visible) myAlertDialog.show();
        }
    
        @Override
        protected String doInBackground(String... arg0)
        {
            // TODO Auto-generated method stub
            return null;
        }
    }
    
    
    @Override
    protected void onResume()
    {
        // TODO Auto-generated method stub
        super.onResume();
        visible = true;
    }
    
    @Override
    protected void onStop()
    {
        visible = false; 
        super.onStop();
    }

1
Merci d'avoir répondu. J'ai en fait utilisé la méthode doInBackground, mais je ne l'ai simplement pas mentionnée ici car elle n'est pas liée à l'alerte. En ce qui concerne l'ajout de builder.create (), cela semble fonctionner correctement, mais je ne sais pas si cela fonctionnera pour tout le monde. Comme II l'a dit plus tôt, mon code actuel fonctionne également bien, mais seulement quelques fois pour quelques utilisateurs, il montre l'impossibilité d'ajouter un problème de fenêtre. Pourriez-vous s'il vous plaît me suggérer quel pourrait être le problème réel dans mon codage ce qui pourrait causer cela?
MSIslam

dans ce cas, l'utilisateur quitte votre activité avant l'appel de onPostExecute, il n'y a donc pas de fenêtre pour contenir la boîte de dialogue, ce qui provoque le blocage de votre application. ajoutez un indicateur à onStop pour savoir si votre activité n'est plus visible, alors ne pas afficher la boîte de dialogue.
moh.sukhni

onPostExecute est en fait appelé, car builder.show () est sous une condition lorsque je vérifie si l'utilisateur n'est pas abonné en fonction du résultat de l'appel de service Web de doInBackground (). Donc, si onPostExecute n'a pas été appelé, il ne serait pas venu jusqu'à la ligne builder.show ().
MSIslam

onPostExecute est appelé par défaut après doInBackground, vous ne pouvez pas l'appeler, et quoi qu'il en soit, il sera exécuté.
moh.sukhni

1
votre tâche asynchrone continuera à fonctionner une fois que l'utilisateur aura quitté votre activité, ce qui obligera builder.show () à cracher votre application car il n'y a aucune activité pour gérer l'interface utilisateur à votre place. votre application extrait des données du Web, mais votre activité a été détruite avant que vous n'obteniez les données.
moh.sukhni

1

Je crée Dialog in onCreateet je l'utilise avec showet hide. Pour moi, la cause principale n'était pas de rejeter onBackPressed, ce qui terminait l' Homeactivité.

@Override
public void onBackPressed() {
new AlertDialog.Builder(this)
                .setTitle("Really Exit?")
                .setMessage("Are you sure you want to exit?")
                .setNegativeButton(android.R.string.no, null)
                .setPositiveButton(android.R.string.yes,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,
                                    int which) {
                                Home.this.finish();
                                return;
                            }
                        }).create().show();

Je terminais l'activité à domicile onBackPressed sans fermer / fermer mes boîtes de dialogue.

Lorsque j'ai fermé mes dialogues, le crash a disparu.

new AlertDialog.Builder(this)
                .setTitle("Really Exit?")
                .setMessage("Are you sure you want to exit?")
                .setNegativeButton(android.R.string.no, null)
                .setPositiveButton(android.R.string.yes,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,
                                    int which) {
                                networkErrorDialog.dismiss() ;
                                homeLocationErrorDialog.dismiss() ;
                                currentLocationErrorDialog.dismiss() ;
                                Home.this.finish();
                                return;
                            }
                        }).create().show();

0

J'essaye cela résolu.

 AlertDialog.Builder builder = new AlertDialog.Builder(
                   this);
            builder.setCancelable(true);
            builder.setTitle("Opss!!");

            builder.setMessage("You Don't have anough coins to withdraw. ");
            builder.setMessage("Please read the Withdraw rules.");
            builder.setInverseBackgroundForced(true);
            builder.setPositiveButton("OK",
                    (dialog, which) -> dialog.dismiss());
            builder.create().show();

-1

Essaye ça :

    public class <class> extends Activity{

    private AlertDialog.Builder builder;

    public void onCreate(Bundle savedInstanceState) {
                    this.requestWindowFeature(Window.FEATURE_NO_TITLE);
                    super.onCreate(savedInstanceState);

                setContentView(R.layout.<view>); 

                builder = new AlertDialog.Builder(<class>.this);
                builder.setCancelable(true);
                builder.setMessage(<message>);
                builder.setInverseBackgroundForced(true);

        //call the <className> class to execute
}

    private class <className> extends AsyncTask<String, Void, String>{

    protected String doInBackground(String... params) {

    }
    protected void onPostExecute(String result){
        if(page.contains("error")) //when not subscribed
        {   
           if(builder!=null){
                builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton){
                    dialog.dismiss();
                        if(!<condition>)
                        {
                        try
                        {
                        String pl = ""; 

                        mHelper.<flow>(<class>.this, SKU, RC_REQUEST, 
                        <listener>, pl);
                        }

                        catch(Exception e)
                        {
                        e.printStackTrace();
                        }
                    }  
                }
            });

            builder.show();
        }
    }

}
}

-4

avec cette idée de variables globales, j'ai enregistré l'instance de MainActivity dans onCreate (); Variable globale Android

public class ApplicationController extends Application {

    public static MainActivity this_MainActivity;
}

et ouvrez une boîte de dialogue comme celle-ci. ça a marché.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Global Var
    globals = (ApplicationController) this.getApplication();
    globals.this_MainActivity = this;
}

et dans un fil, j'ouvre un dialogue comme celui-ci.

AlertDialog.Builder alert = new AlertDialog.Builder(globals.this_MainActivity);
  1. Ouvrez MainActivity
  2. Démarrez un fil.
  3. Ouvrir la boîte de dialogue à partir du fil -> travailler.
  4. Cliquez sur le bouton "Retour" (onCreate sera appelé et supprimera le premier MainActivity)
  5. La nouvelle MainActivity démarre. (et enregistrez son instance dans les globaux)
  6. Ouvrez la boîte de dialogue à partir du premier fil -> elle s'ouvrira et fonctionnera

:)


4
Ne gardez jamais une référence statique à une activité. Cela entraînera une fuite de mémoire
Leandroid
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.