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

MSI*_*lam 107 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();
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我还看到了另一个警报中的错误,我没有转发到任何其他警报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();
}
Run Code Online (Sandbox Code Playgroud)

Rit*_*une 247

android.view.WindowManager$BadTokenException: Unable to add window"
Run Code Online (Sandbox Code Playgroud)

问题:

当应用程序尝试通过打开对话框从后台线程(AsyncTask)通知用户时,会发生此异常.

如果你试图从后台线程修改UI(通常来自AsyncTask的onPostExecute())并且如果活动进入完成阶段,即明确调用finish(),用户按下主页或后退按钮或由Android制作的活动清理然后你得到这个错误.

原因:

此异常的原因是,正如异常消息所示,活动已完成,但您正在尝试显示包含已完成活动的上下文的对话框.由于没有窗口让对话框显示android运行时抛出此异常.

解:

使用isFinishing()Android调用的方法来检查此活动是否正在完成:是由Android进行的显式finish()调用或活动清理.通过使用此方法,可以很容易地避免在活动完成时从后台线程打开对话框.

还要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();
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

更新:

窗口令牌:

顾名思义,窗口标记是一种特殊类型的Binder标记,窗口管理器使用它来唯一地标识系统中的窗口.窗口令牌对于安全性非常重要,因为它们使恶意应用程序无法在其他应用程序的窗口上绘制.窗口管理器通过要求应用程序传递其应用程序的窗口令牌作为每个添加或删除窗口的请求的一部分来防止这种情况.如果令牌不匹配,则窗口管理器拒绝该请求并抛出 BadTokenException.如果没有窗口令牌,则无法进行必要的识别步骤,窗口管理器将无法保护自己免受恶意应用程序的攻击.

 真实场景:

当应用程序第一次启动时,  ActivityManagerService  会创建一种称为应用程序窗口令牌的特殊窗口令牌,它唯一地标识应用程序的顶级容器窗口.活动管理器将此令牌提供给应用程序和窗口管理器,并且应用程序每次想要向窗口添加新窗口时都将令牌发送到窗口管理器.这确保了应用程序和窗口管理器之间的安全交互(通过使其无法在其他应用程序之上添加窗口),并且还使活动管理器可以轻松地向窗口管理器发出直接请求.

  • 可怕的错误!!我正在学习一个教程,作为@PhilRoggenbuck,我的问题是在调用 StartActivity(...) 之前调用 Toast..Show() 引起的。为了解决这个问题,我在新调用的活动中移动了吐司! (2认同)

Jem*_*rov 19

我有对话框显示功能:

void showDialog(){
    new AlertDialog.Builder(MyActivity.this)
    ...
    .show();
}
Run Code Online (Sandbox Code Playgroud)

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

if(!isFinishing())
    showDialog();
Run Code Online (Sandbox Code Playgroud)

  • 如果Android已经完成,为什么Android会运行任何代码?如果我们遵循这个解决方案,那么想象我们应该多少时间使用isFinishing来避免类似的问题. (2认同)

Rag*_*dra 9

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

例如

而不是这个.

AlertDialog alertDialog = new AlertDialog.Builder(this).create();
Run Code Online (Sandbox Code Playgroud)

试着用

AlertDialog alertDialog = new AlertDialog.Builder(FirstActivity.getInstance()).create();
Run Code Online (Sandbox Code Playgroud)