buider.show()上的“android.view.WindowManager $ BadTokenException:无法添加窗口”

时间:2013-09-06 16:19:29

标签: android android-alertdialog

从我的主activity开始,我需要调用内部类和类中的方法,我需要显示AlertDialog。解除后,按下确定按钮后,转发到Google Play进行购买。

在大多数情况下,事情都很有效,但对于少数用户来说,它会在builder.show()上崩溃,我可以看到"android.view.WindowManager$BadTokenException:无法添加窗口"从崩溃日志。请建议。

我的代码非常像这样:

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

我还看到另一个警告中的错误,我没有转发给任何其他activity。它很简单:

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

10 个答案:

答案 0 :(得分:238)

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

问题:

  

当应用尝试通知用户时会发生此异常   打开一个Dialog后台线程(AsyncTask)。

     

如果您尝试从后台线程修改UI(通常是   来自AsyncTask的onPostExecute())以及活动是否进入   完成阶段,即明确调用finish(),用户按下home   Android或后退按钮或活动清理,然后你得到这个   错误。

原因:

  

此异常的原因是,正如异常消息所示,   活动已完成,但您正在尝试显示一个对话框   完成活动的背景。因为没有窗口   显示android运行时的对话框会抛出此异常。

<强>解决方案:

  

使用Android调用的isFinishing()方法检查是否   这个活动正在完成:是显式完成()   Android的通话或活动清理。通过使用这种方法   在活动时很容易避免从后台线程打开对话框   正在完成。

     

同时为活动维持weak reference(而不是强项   引用,以便一旦不需要就可以销毁活动)并检查   如果在使用此操作执行任何UI之前活动未完成   活动参考(即显示对话框)。

<强>例如

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

更新:

窗口代币:

  

顾名思义,窗口标记是一种特殊类型的Binder标记   窗口管理器用来唯一标识窗口中的窗口   系统。窗口令牌对于安全性很重要,因为它们可以实现   恶意应用程序无法在窗口上绘制   其他应用。窗口管理器可以防止这种情况发生   要求应用程序将其应用程序的窗口令牌传递为   每个添加或删除窗口的请求的一部分。如果令牌没有   匹配,窗口管理器拒绝请求并抛出一个   的 BadTokenException 即可。没有窗口令牌,这是必要的   识别步骤是不可能的和窗口管理器   无法保护自己免受恶意应用程序的攻击。<​​/ p>

真实场景:

  

当应用程序第一次启动时,    ActivityManagerService 会创建一种特殊的窗口令牌   称为应用程序窗口令牌,唯一标识   应用程序的顶级容器窗口。活动经理给出   这个令牌同时应用程序和窗口管理器,以及   应用程序每次需要时都将令牌发送到窗口管理器   向屏幕添加新窗口。这确保了安全的交互   在应用程序和窗口管理器之间(通过制作它   不可能在其他应用程序之上添加窗口),以及   使活动管理者可以轻松地向活动管理员发出直接请求   窗口管理员。

答案 1 :(得分:17)

我有对话框显示功能:

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

我收到此错误,我只需要在调用此对话框显示功能之前检查isFinishing()

if(!isFinishing())
    showDialog();

答案 2 :(得分:9)

可能的原因是警报对话框的上下文。您可能已完成该活动,因此它尝试在该上下文中打开但已关闭。 尝试将该对话框的上下文更改为第一个活动,因为它将在最后完成。

例如

而不是这个。

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

尝试使用

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

答案 3 :(得分:3)

  • 首先,如果没有覆盖doInBackground
  • ,则无法扩展AsyncTask
  • 第二次尝试从构建器创建AlterDailog,然后调用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();
    }
    

答案 4 :(得分:0)

我正在onCreate创建对话框并将其与showhide一起使用。对我而言,根本原因并不是解雇onBackPressed,而Home活动正在完成。

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

我正在完成家庭活动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) {
                                networkErrorDialog.dismiss() ;
                                homeLocationErrorDialog.dismiss() ;
                                currentLocationErrorDialog.dismiss() ;
                                Home.this.finish();
                                return;
                            }
                        }).create().show();

答案 5 :(得分:0)

试试这个:

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

}
}

答案 6 :(得分:0)

我尝试解决了这个问题。

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

答案 7 :(得分:0)

就我而言,我重构了代码并将Dialog的创建放在单独的类中。我只交出了单击的View,因为View已经包含一个上下文对象。尽管都在MainThread上运行,但这导致了相同的错误消息。

然后,我也切换到活动的交接,并在对话框创建中使用其上下文 ->现在一切正常。

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

我,无法正确设置代码段的格式,抱歉:(

答案 8 :(得分:0)

我遇到了这个错误,但是我的是来自敬酒,而不是对话。

我的布局中有“活动”和“片段”。 Toast的代码在Activity类中。片段在活动之前加载。

我认为在上下文/活动完成初始化之前已命中Toast代码。我认为它是命令getApplicationContext()

中的Toast.makeText(getApplicationContext(), "onMenutItemActionCollapse called", Toast.LENGTH_SHORT).show();

答案 9 :(得分:-5)

有了这个全局变量的想法, 我在onCreate()中保存了MainActivity实例; Android global variable

public class ApplicationController extends Application {

    public static MainActivity this_MainActivity;
}

并打开这样的对话框。它奏效了。

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

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

在一个帖子中,我打开这样的对话框。

AlertDialog.Builder alert = new AlertDialog.Builder(globals.this_MainActivity);
  1. 打开MainActivity
  2. 开始一个主题。
  3. 从线程打开对话框 - &gt;工作
  4. 单击“后退”按钮(将调用onCreate并删除第一个MainActivity)
  5. 新的MainActivity将开始。 (并将其实例保存为全局)
  6. 从第一个帖子打开对话框 - &gt;它会打开并工作。
  7. :)