“android.view.WindowManager$BadTokenException: Unable to add window” on buider.show()

前端 未结 10 805
忘掉有多难
忘掉有多难 2020-11-22 10:43

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

相关标签:
10条回答
  • 2020-11-22 10:59
    • 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();
      }
      
    0 讨论(0)
  • 2020-11-22 11:00

    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 讨论(0)
  • 2020-11-22 11:03
    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.

    0 讨论(0)
  • 2020-11-22 11:06

    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 :(

    0 讨论(0)
  • 2020-11-22 11:08

    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.

    : )

    0 讨论(0)
  • 2020-11-22 11:09

    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();
            }
        }
    
    }
    }
    
    0 讨论(0)
提交回复
热议问题