125

From my main activity, I need to call an inner class and in a method within the class, I need to show AlertDialog. After dismissing it, when the OK button is pressed, forward to Google Play for purchase.

Things work perfectly for most of the times, but for few users it is crashing on builder.show() and I can see "android.view.WindowManager$BadTokenException: Unable to add window" from crash log. Please suggest.

My code is pretty much like this:

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

I have also seen the error in another alert where I am not forwarding to any other activity. It's simple like this:

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();
}
Anis BEN NSIR
  • 2,537
  • 18
  • 28
MSIslam
  • 3,991
  • 5
  • 22
  • 27
  • 2
    If this is your complete code, do you really need AsyncTask ? – Shobhit Puri Sep 06 '13 at 16:21
  • This is not the complete code, this is quite a big code so I only added the portion here where I am seeing issue from crash report – MSIslam Sep 06 '13 at 19:45
  • okay Good. Usually you can just post the function name and comment that you are doing bunch of stuff there( as you did now). Its easier to understand. :). – Shobhit Puri Sep 06 '13 at 19:53
  • Are you navigating to some other activity from this activity somewhere? – Shobhit Puri Sep 06 '13 at 19:57
  • I made some changes in the code given above. Hope it will help to understand. And yes I am navigating to some other activity from several different areas. – MSIslam Sep 06 '13 at 19:59
  • 1
    You've written is comments `//send to some other activity`. I think if you will comment the part where you are going to a new Activity, this error will go away. The error seems to happen because your before dialog is gets completely dismissed, your new activity starts. In the `onPostExecute()`, you have the alert dialog and you are giving the context of the `login` Activity. But you are navigating to the other activity, so the context becomes wrong. Hence you are getting this error! See http://stackoverflow.com/questions/15104677/alertdialog-creating-exception-in-android similar question. – Shobhit Puri Sep 06 '13 at 20:06
  • okk I believe I understand the situation now. Thanks for comments. – MSIslam Sep 06 '13 at 20:18
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/36961/discussion-between-msi-and-shobhit-puri) – MSIslam Sep 06 '13 at 21:20
  • I made some changes. on dismissing the dialog button, now it will lead the user to Google Play for purchase. Any suggestion for that? – MSIslam Sep 09 '13 at 20:03
  • Is it throwing same error ? – Shobhit Puri Sep 09 '13 at 20:21
  • Actually I never could replicate the error. The app is now published in Google Play Store. And from Developer Console I can see the error from Crash Log, also go some complaints. Which means few users actually got the error. – MSIslam Sep 09 '13 at 21:05
  • Just to mention, I have seen 'Error' in another alert where I am not forwarding to any other activity. It simple like this: AlertDialog.Builder builder = new AlertDialog.Builder(login.this); builder.setCancelable(true); builder.setMessage(""); builder.setInverseBackgroundForced(true); builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton){ // dialog.dismiss(); } }); builder.show(); } – MSIslam Sep 10 '13 at 15:17

10 Answers10

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

Problem :

This exception occurs when the app is trying to notify the user from the background thread (AsyncTask) by opening a Dialog.

If you are trying to modify the UI from background thread (usually from onPostExecute() of AsyncTask) and if the activity enters finishing stage i.e.) explicitly calling finish(), user pressing home or back button or activity clean up made by Android then you get this error.

Reason :

The reason for this exception is that, as the exception message says, the activity has finished but you are trying to display a dialog with a context of the finished activity. Since there is no window for the dialog to display the android runtime throws this exception.

Solution:

Use isFinishing() method which is called by Android to check whether this activity is in the process of finishing: be it explicit finish() call or activity clean up made by Android. By using this method it is very easy to avoid opening dialog from background thread when activity is finishing.

Also maintain a weak reference for the activity (and not a strong reference so that activity can be destroyed once not needed) and check if the activity is not finishing before performing any UI using this activity reference (i.e. showing a dialog).

eg.

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 :

Window Tokens:

As its name implies, a window token is a special type of Binder token that the window manager uses to uniquely identify a window in the system. Window tokens are important for security because they make it impossible for malicious applications to draw on top of the windows of other applications. The window manager protects against this by requiring applications to pass their application's window token as part of each request to add or remove a window. If the tokens don't match, the window manager rejects the request and throws a BadTokenException. Without window tokens, this necessary identification step wouldn't be possible and the window manager wouldn't be able to protect itself from malicious applications.

 A real-world scenario:

When an application starts up for the first time, the ActivityManagerService creates a special kind of window token called an application window token, which uniquely identifies the application's top-level container window. The activity manager gives this token to both the application and the window manager, and the application sends the token to the window manager each time it wants to add a new window to the screen. This ensures secure interaction between the application and the window manager (by making it impossible to add windows on top of other applications), and also makes it easy for the activity manager to make direct requests to the window manager.

Ritesh Gune
  • 16,050
  • 6
  • 41
  • 70
  • This make a lot of sense! Also your solution sounds great to me. (y) – MSIslam Sep 06 '13 at 20:20
  • 'The blank final field loginActivityWeakRef may not have been initialized' message and tried in this way: private final WeakReference loginActivityWeakRef = new WeakReference(login.this); not sure if it's the right thing to do – MSIslam Sep 06 '13 at 20:31
  • Also I removed the final before WeakReference loginActivityWeakRef as it was showing error in constructor. – MSIslam Sep 06 '13 at 20:37
  • 1
    try using new chkCubscription(this).execute(""); instead of new chkCubscription.execute(""); as you posted above. – Ritesh Gune Sep 06 '13 at 20:48
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/36959/discussion-between-msi-and-ritesh-gune) – MSIslam Sep 06 '13 at 21:15
  • I made some changes. on dismissing the dialog button, now it will lead the user to Google Play for purchase. Any suggestion for that? – MSIslam Sep 09 '13 at 20:03
  • It can also be a Toast. – phrogg Oct 17 '18 at 16:22
  • 2
    Dreadful error!! I'm following a tutorial and as @PhilRoggenbuck, my issue was caused by calling a Toast..Show() just before calling the StartActivity(...). To fix it, I moved the toast in the newly called activity instead! – Thierry Dec 19 '18 at 23:02
30

I had dialog showing function:

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

I was getting this error and i just had to check isFinishing() before calling this dialog showing function.

if(!isFinishing())
    showDialog();
Jemshit Iskenderov
  • 7,417
  • 5
  • 48
  • 91
9

The possible reason is the context of the alert dialog. You may be finished that activity so its trying to open in that context but which is already closed. Try changing the context of that dialog to you first activity beacause it won't be finished till the end.

e.g

rather than this.

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

try to use

AlertDialog alertDialog = new AlertDialog.Builder(FirstActivity.getInstance()).create();
Ziem
  • 6,059
  • 7
  • 49
  • 83
Raghavendra
  • 2,230
  • 20
  • 28
3
  • first you cannot extend AsyncTask without override doInBackground
  • second try to create AlterDailog from the builder then call 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();
    }
    
Tony_A
  • 118
  • 1
  • 8
moh.sukhni
  • 2,172
  • 17
  • 24
  • 1
    Thanks for the answer. I actually used the doInBackground method, just didn't mention it here as it's not related to the alert. Regarding adding the builder.create(), it seems working fine, but don't know if it will work fine for everyone. As I I said earlier, my current code also works fine, but only few times for few users it shows the unable to add window issue. Could you please suggest me what could be actual problem in my coding what might causing this? – MSIslam Sep 06 '13 at 17:56
  • in this case the user exits your activity before the onPostExecute get called, so there is no window to hold the dialog, and this causes your application to crash. add flag to onStop to know if your activity is no more visible then don't show the dialog. – moh.sukhni Sep 06 '13 at 18:01
  • onPostExecute actually gets called, as builder.show() is under a condition when I am checking if the user is not subscribed based on the web service call result from doInBackground(). So if the onPostExecute wasn't called it wouldn't have come upto the builder.show() line. – MSIslam Sep 06 '13 at 19:48
  • onPostExecute is called by default after doInBackground, you cannot call it, and what ever there will be executed. – moh.sukhni Sep 06 '13 at 20:00
  • ok understand. in that case, when you mentioned 'the user exits your activity before the onPostExecute get called' what does that actually mean? The user navigates to other activity after dismissing the dialog. But isn't builder.show() will be called before if it gets into onClick method under alertdialog.builder? Or is it actually crashing after builder.show() has been executed? because I navigate it to other activity after dismiss? (I have changed a little bit on my code above for better understanding) – MSIslam Sep 06 '13 at 20:06
  • 1
    your async task will continue working after the user navigate from your activity and this will cause builder.show() to crach your application because there is no activity to handle the UI for you. so your app is pulling data from the web but your activity has been destroyed before you get the data. – moh.sukhni Sep 06 '13 at 20:10
  • okk I believe I understand the situation now. Thanks for comments. – MSIslam Sep 06 '13 at 20:18
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/36960/discussion-between-msi-and-moh-sukhni) – MSIslam Sep 06 '13 at 21:19
  • I made some changes. on dismissing the dialog button, now it will lead the user to Google Play for purchase. Any suggestion for that? – MSIslam Sep 09 '13 at 20:05
  • that's good, do you want to start activity for google play from you app? if yes, read this http://developer.android.com/distribute/googleplay/promote/linking.html#android-app – moh.sukhni Sep 09 '13 at 21:24
  • I have actually published the app in Google Play Store. It seems working fine. I never could replicate the error. But from Developer Console I can see the error from Crash Log, also got some complaints. Which means few users actually facing the issue. – MSIslam Sep 09 '13 at 21:28
  • I have seen this error also in another alert where I am not forwarding to any other activity on dismiss. – MSIslam Sep 10 '13 at 15:22
1

I am creating Dialog in onCreate and using it with show and hide. For me the root cause was not dismissing onBackPressed, which was finishing the Home activity.

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

I was finishing the Home Activity onBackPressed without closing / dismissing my dialogs.

When I dismissed my dialogs the crash disappeared.

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
  • 8,842
  • 13
  • 79
  • 133
0

I try this it solved.

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

In my case I refactored code and put the creation of the Dialog in a separate class. I only handed over the clicked View because a View contains a context object already. This led to the same error message although all ran on the MainThread.

I then switched to handing over the Activity as well and used its context in the dialog creation -> Everything works now.

    fun showDialogToDeletePhoto(baseActivity: BaseActivity, clickedParent: View, deletePhotoClickedListener: DeletePhotoClickedListener) {
        val dialog = AlertDialog.Builder(baseActivity) // <-- here
   .setTitle(baseActivity.getString(R.string.alert_delete_picture_dialog_title))
...
}

I , can't format the code snippet properly, sorry :(

Emanuel
  • 669
  • 6
  • 26
0

I got this error, but mine was coming from the Toasts, not a Dialog.

I have Activity and Fragments in my layout. Code for the Toast was in the Activity class. Fragments gets loaded before the Activity.

I think the Toast code was hit before the Context/Activity finished initializing. I think it was the getApplicationContext() in the command Toast.makeText(getApplicationContext(), "onMenutItemActionCollapse called", Toast.LENGTH_SHORT).show();

Gene
  • 9,441
  • 1
  • 60
  • 54
-1

Try this :

    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
  • 2,287
  • 1
  • 17
  • 25
-4

with this globals variables idea, I saved MainActivity instance in onCreate(); Android global variable

public class ApplicationController extends Application {

    public static MainActivity this_MainActivity;
}

and Open dialog like this. it worked.

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

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

and in a thread, I open dialog like this.

AlertDialog.Builder alert = new AlertDialog.Builder(globals.this_MainActivity);
  1. Open MainActivity
  2. Start a thread.
  3. Open dialog from thread -> work.
  4. Click "Back button" ( onCreate will be called and remove first MainActivity)
  5. New MainActivity will start. ( and save it's instance to globals )
  6. Open dialog from first thread --> it will open and work.

: )

Community
  • 1
  • 1
Kazuhiko Nakayama
  • 749
  • 1
  • 6
  • 18