"Android.view.WindowManager $ BadTokenException: Fenster kann nicht hinzugefügt werden" auf buider.show ()

118

Von meinem Hauptteil aus activitymuss ich eine innere Klasse aufrufen und in einer Methode innerhalb der Klasse muss ich zeigen AlertDialog. Wenn Sie nach dem Schließen die OK-Taste drücken, leiten Sie sie zum Kauf an Google Play weiter.

Die Dinge funktionieren meistens perfekt, aber für einige Benutzer stürzt es ab builder.show()und ich kann im "android.view.WindowManager$BadTokenException:Absturzprotokoll sehen, dass kein Fenster hinzugefügt werden kann . Bitte vorschlagen.

Mein Code ist ziemlich ähnlich:

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();
      }
    }
  }
}

Ich habe den Fehler auch in einer anderen Warnung gesehen, in der ich nicht an eine andere weiterleite activity. So einfach ist das:

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();
}
MSIslam
quelle
2
Wenn dies Ihr vollständiger Code ist, benötigen Sie wirklich AsyncTask?
Shobhit Puri
Dies ist nicht der vollständige Code, dies ist ein ziemlich großer Code, daher habe ich hier nur den Teil hinzugefügt, in dem ich ein Problem aus dem
Absturzbericht sehe
OK gut. Normalerweise können Sie einfach den Funktionsnamen posten und kommentieren, dass Sie dort eine Menge Dinge tun (wie Sie es jetzt getan haben). Es ist leichter zu verstehen. :).
Shobhit Puri
Navigieren Sie von dieser Aktivität zu einer anderen Aktivität?
Shobhit Puri
1
Sie haben Kommentare geschrieben //send to some other activity. Ich denke, wenn Sie den Teil kommentieren, in dem Sie zu einer neuen Aktivität gehen, wird dieser Fehler verschwinden. Der Fehler scheint aufzutreten, da Ihr vorheriger Dialog vollständig geschlossen wird und Ihre neue Aktivität beginnt. In der onPostExecute()haben Sie den Warndialog und geben den Kontext der loginAktivität an. Sie navigieren jedoch zu der anderen Aktivität, sodass der Kontext falsch wird. Daher erhalten Sie diesen Fehler! Siehe stackoverflow.com/questions/15104677/… ähnliche Frage.
Shobhit Puri

Antworten:

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

Problem :

Diese Ausnahme tritt auf, wenn die App versucht, den Benutzer über den Hintergrundthread (AsyncTask) zu benachrichtigen, indem ein Dialogfeld geöffnet wird.

Wenn Sie versuchen, die Benutzeroberfläche über den Hintergrundthread (normalerweise über onPostExecute () von AsyncTask) zu ändern, und wenn die Aktivität in die Endphase eintritt, dh explizit finish () aufruft, drücken Sie die Home- oder Back-Taste oder die von Android vorgenommene Bereinigung der Aktivität erhalte diesen Fehler.

Grund :

Der Grund für diese Ausnahme ist, dass, wie in der Ausnahmemeldung angegeben, die Aktivität beendet wurde, Sie jedoch versuchen, einen Dialog mit einem Kontext der abgeschlossenen Aktivität anzuzeigen. Da es kein Fenster für den Dialog gibt, in dem die Android-Laufzeit angezeigt wird, wird diese Ausnahme ausgelöst.

Lösung:

Verwenden Sie isFinishing()die von Android aufgerufene Methode, um zu überprüfen, ob diese Aktivität gerade beendet wird: sei es ein expliziter Aufruf von finish () oder eine Bereinigung der Aktivität von Android. Mit dieser Methode ist es sehr einfach zu vermeiden, dass der Dialog aus dem Hintergrund-Thread geöffnet wird, wenn die Aktivität beendet ist.

Behalten Sie außerdem ein weak referencefür die Aktivität bei (und keine starke Referenz, damit die Aktivität zerstört werden kann, sobald sie nicht benötigt wird) und prüfen Sie, ob die Aktivität nicht beendet ist, bevor Sie eine Benutzeroberfläche mit dieser Aktivitätsreferenz ausführen (dh ein Dialogfeld anzeigen).

zB .

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();
      }
    }
  }
}

Update:

Fenstertoken:

Wie der Name schon sagt, ist ein Fenstertoken eine spezielle Art von Binder-Token, mit der der Fenstermanager ein Fenster im System eindeutig identifiziert. Fenstertoken sind für die Sicherheit wichtig, da sie es böswilligen Anwendungen unmöglich machen, auf die Fenster anderer Anwendungen zu zeichnen. Der Fenstermanager schützt davor, indem Anwendungen das Fenstertoken ihrer Anwendung als Teil jeder Anforderung zum Hinzufügen oder Entfernen eines Fensters übergeben müssen. Wenn die Token nicht übereinstimmen, lehnt der Fenstermanager die Anforderung ab und löst eine BadTokenException aus . Ohne Fenstertoken wäre dieser notwendige Identifikationsschritt nicht möglich und der Fenstermanager wäre nicht in der Lage, sich vor schädlichen Anwendungen zu schützen.

 Ein reales Szenario:

Wenn eine Anwendung zum ersten Mal gestartet wird,  erstellt der  ActivityManagerService eine spezielle Art von Fenstertoken, das als Anwendungsfenstertoken bezeichnet wird und das Containerfenster der obersten Ebene der Anwendung eindeutig identifiziert. Der Aktivitätsmanager gibt dieses Token sowohl an die Anwendung als auch an den Fenstermanager weiter, und die Anwendung sendet das Token jedes Mal an den Fenstermanager, wenn sie dem Bildschirm ein neues Fenster hinzufügen möchte. Dies gewährleistet eine sichere Interaktion zwischen der Anwendung und dem Fenstermanager (indem das Hinzufügen von Fenstern zu anderen Anwendungen unmöglich gemacht wird) und erleichtert es dem Aktivitätsmanager, direkte Anforderungen an den Fenstermanager zu richten.

Ritesh Gune
quelle
Das macht sehr viel Sinn! Auch Ihre Lösung klingt für mich großartig. (y)
MSIslam
Die Meldung "Das leere letzte Feld loginActivityWeakRef wurde möglicherweise nicht initialisiert" wurde auf folgende Weise versucht: private final WeakReference <Login> loginActivityWeakRef = new WeakReference <Login> (login.this);
Ich bin
Außerdem habe ich das Finale vor WeakReference <login> loginActivityWeakRef entfernt, da es einen Fehler im Konstruktor zeigte.
MSIslam
1
versuche es mit new chkCubscription (this) .execute (""); anstelle von new chkCubscription.execute (""); wie du oben gepostet hast.
Ritesh Gune
2
Schrecklicher Fehler !! Ich folge einem Tutorial und als @PhilRoggenbuck wurde mein Problem durch das Aufrufen eines Toast..Show () kurz vor dem Aufrufen der StartActivity (...) verursacht. Um das Problem zu beheben, habe ich stattdessen den Toast in der neu aufgerufenen Aktivität verschoben!
Thierry
26

Ich hatte einen Dialog, der die Funktion zeigte:

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

Ich habe diesen Fehler erhalten und musste ihn nur überprüfen, isFinishing()bevor ich diesen Dialog mit der Funktion aufrief.

if(!isFinishing())
    showDialog();
Jemshit Iskenderov
quelle
1
sollten wir nicht schreiben if(!MyActivity.this.isFinishing())? Wenn es in MyActivity nicht richtig ist
Bibaswann Bandyopadhyay
2
Warum sollte Android Code ausführen, wenn er bereits fertig ist? Wenn wir dieser Lösung folgen, stellen Sie sich vor, wie oft wir isFinishing wirklich verwenden sollten, um ähnliche Probleme zu vermeiden.
David
@ David Ich denke, dass hier einige Details fehlen, wie zum Beispiel der Dialog, der in einem Hintergrund-Thread aufgerufen wird, aber ich stimme Ihrem Standpunkt in vollem Umfang zu.
Verlassener Wagen
Toller Punkt, warum zum Teufel sollte ich nach isFinishing suchen müssen!
Chibueze Opata
9

Der mögliche Grund ist der Kontext des Alarmdialogs. Möglicherweise haben Sie diese Aktivität beendet, sodass versucht wird, sie in diesem Kontext zu öffnen, der jedoch bereits geschlossen ist. Versuchen Sie, den Kontext dieses Dialogfelds auf Ihre erste Aktivität zu ändern, da er erst am Ende abgeschlossen sein wird.

z.B

lieber als das.

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

versuchen zu benutzen

AlertDialog alertDialog = new AlertDialog.Builder(FirstActivity.getInstance()).create();
Raghavendra
quelle
3
  • Erstens können Sie AsyncTask nicht erweitern, ohne doInBackground zu überschreiben
  • Versuchen Sie anschließend, AlterDailog aus dem Builder zu erstellen, und rufen Sie dann show () auf.

    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();
    }
moh.sukhni
quelle
1
Danke für die Antwort. Ich habe tatsächlich die doInBackground-Methode verwendet und sie hier nur nicht erwähnt, da sie nicht mit der Warnung zusammenhängt. In Bezug auf das Hinzufügen von builder.create () scheint es gut zu funktionieren, aber ich weiß nicht, ob es für alle gut funktioniert. Wie ich bereits sagte, funktioniert mein aktueller Code auch einwandfrei, aber nur wenige Male für wenige Benutzer zeigt er das Problem, dass kein Fenster hinzugefügt werden kann. Könnten Sie mir bitte vorschlagen, was tatsächlich ein Problem in meiner Codierung sein könnte, was dies verursachen könnte?
MSIslam
In diesem Fall beendet der Benutzer Ihre Aktivität, bevor onPostExecute aufgerufen wird. Daher gibt es kein Fenster, in dem der Dialog gehalten werden kann, und dies führt zum Absturz Ihrer Anwendung. Fügen Sie onStop ein Flag hinzu, um festzustellen, ob Ihre Aktivität nicht mehr sichtbar ist, und zeigen Sie den Dialog dann nicht an.
moh.sukhni
onPostExecute wird tatsächlich aufgerufen, da builder.show () unter einer Bedingung steht, wenn ich überprüfe, ob der Benutzer nicht abonniert ist, basierend auf dem Ergebnis des Webdienstaufrufs von doInBackground (). Wenn also onPostExecute nicht aufgerufen worden wäre, wäre es nicht bis zur Zeile builder.show () gekommen.
MSIslam
onPostExecute wird standardmäßig nach doInBackground aufgerufen, Sie können es nicht aufrufen und was auch immer dort ausgeführt wird.
moh.sukhni
1
Ihre asynchrone Aufgabe funktioniert weiter, nachdem der Benutzer von Ihrer Aktivität aus navigiert hat. Dies führt dazu, dass builder.show () Ihre Anwendung beendet, da keine Aktivität für die Benutzeroberfläche vorhanden ist. Ihre App ruft also Daten aus dem Web ab, aber Ihre Aktivität wurde zerstört, bevor Sie die Daten erhalten.
moh.sukhni
1

Ich erstelle einen Dialog in onCreateund benutze ihn mit showund hide. Für mich war die Grundursache nicht das Entlassen onBackPressed, was die HomeAktivität beendete.

@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();

Ich habe die Heimaktivität beendet, onBackPressedohne meine Dialoge zu schließen / zu schließen.

Als ich meine Dialoge schloss, verschwand der Absturz.

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();
Siddharth
quelle
0

Ich versuche das zu lösen.

 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();
Himanshu Kumar
quelle
-1

Versuche dies :

    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();
        }
    }

}
}
pathe.kiran
quelle
-4

Mit dieser Idee für globale Variablen habe ich die MainActivity-Instanz in onCreate () gespeichert. Globale Android-Variable

public class ApplicationController extends Application {

    public static MainActivity this_MainActivity;
}

und Dialog wie folgt öffnen. es funktionierte.

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

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

und in einem Thread öffne ich einen solchen Dialog.

AlertDialog.Builder alert = new AlertDialog.Builder(globals.this_MainActivity);
  1. Öffnen Sie MainActivity
  2. Starten Sie einen Thread.
  3. Dialog vom Thread öffnen -> arbeiten.
  4. Klicken Sie auf "Zurück" (onCreate wird aufgerufen und erste MainActivity entfernt)
  5. Neue MainActivity wird gestartet. (und speichern Sie die Instanz in Globals)
  6. Dialog vom ersten Thread öffnen -> es wird geöffnet und funktioniert.

:)

Kazuhiko Nakayama
quelle
4
Behalten Sie niemals einen statischen Verweis auf eine Aktivität. Es wird Speicherverlust verursachen
Leandroid