【问题标题】:"android.view.WindowManager$BadTokenException: Unable to add window" on buider.show()buider.show() 上的“android.view.WindowManager$BadTokenException:无法添加窗口”
【发布时间】:2013-09-10 19:37:07
【问题描述】:

从我的主要activity,我需要调用一个内部类,并且在类内的一个方法中,我需要显示AlertDialog。关闭后,按下 OK 按钮后,转到 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();
}

【问题讨论】:

  • 如果这是你的完整代码,你真的需要 AsyncTask 吗?
  • 这不是完整的代码,这是一个相当大的代码,所以我只添加了我在崩溃报告中看到问题的部分
  • 好的好的。通常你可以只发布函数名称并评论你正在那里做很多事情(就像你现在所做的那样)。它更容易理解。 :)。
  • 您是否正在某个地方从该活动导航到其他活动?
  • 你写的是 cmets //send to some other activity。我想如果你评论你要去一个新活动的部分,这个错误就会消失。该错误似乎发生了,因为您之前的对话框被完全关闭,您的新活动开始了。在onPostExecute() 中,您有警报对话框,并且您正在提供login 活动的上下文。但是您正在导航到其他活动,因此上下文变得错误。因此,您收到此错误!见stackoverflow.com/questions/15104677/…类似问题。

标签: android android-alertdialog


【解决方案1】:
android.view.WindowManager$BadTokenException: Unable to add window"

问题:

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

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

原因:

这个异常的原因是,正如异常消息所说, 活动已完成,但您正在尝试显示一个对话框 已完成活动的上下文。由于没有窗户 显示 android 运行时的对话框会引发此异常。

解决方案:

使用Android调用的isFinishing()方法检查是否 此活动正在完成中:无论是显式的finish() 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。没有窗口令牌,这是必要的 识别步骤是不可能的,窗口管理器 将无法保护自己免受恶意应用程序的侵害。

 真实场景:

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

【讨论】:

  • 这很有意义!你的解决方案对我来说也很棒。 (y)
  • 'The blank final field loginActivityWeakRef may not have been initialized'消息并尝试了这种方式:private final WeakReference loginActivityWeakRef = new WeakReference(login.this);不确定这样做是否正确
  • 尝试使用新的 chkCubscription(this).execute("");而不是 new chkCubscription.execute("");正如您在上面发布的那样。
  • 也可以是Toast。
  • 可怕的错误!!我正在学习一个教程,作为@PhilRoggenbuck,我的问题是由在调用 StartActivity(...) 之前调用 Toast..Show() 引起的。为了解决这个问题,我将 toast 移到了新调用的活动中!
【解决方案2】:

我有对话框显示功能:

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

我遇到了这个错误,我只需要在调用这个对话框显示函数之前检查isFinishing()

if(!isFinishing())
    showDialog();

【讨论】:

  • 我们不应该写if(!MyActivity.this.isFinishing())吗?如果在 MyActivity 中不正确
  • 如果代码已经完成,为什么 Android 还要运行它?如果我们遵循这个解决方案,那么想象一下我们应该真正使用多少次 isFinishing 来避免类似的问题。
  • @David 我认为这缺少一些细节,例如在后台线程中调用的对话框,但我完全同意你现在的观点。
  • 很好,为什么我需要检查 isFinishing!
【解决方案3】:

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

例如

而不是这个。

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

尝试使用

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

【讨论】:

    【解决方案4】:
    • 首先你不能在不覆盖 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();
      }
      

    【讨论】:

    • 感谢您的回答。我实际上使用了 doInBackground 方法,只是在这里没有提到它,因为它与警报无关。关于添加builder.create(),它似乎工作正常,但不知道它是否适合每个人。正如我之前所说,我当前的代码也可以正常工作,但对于少数用户来说只有几次它显示了无法添加窗口的问题。您能否建议我在我的编码中可能是什么实际问题可能导致这种情况?
    • 在这种情况下,用户在调用 onPostExecute 之前退出了您的活动,因此没有窗口可以容纳对话框,这会导致您的应用程序崩溃。向 onStop 添加标志以了解您的活动是否不再可见,然后不显示对话框。
    • onPostExecute 实际上被调用了,因为 builder.show() 处于根据 doInBackground() 的 Web 服务调用结果检查用户是否未订阅的情况下。因此,如果不调用 onPostExecute,它就不会出现在 builder.show() 行中。
    • onPostExecute 默认是在doInBackground 之后调用的,不能调用,什么都会执行。
    • 您的异步任务将在用户从您的活动导航后继续工作,这将导致 builder.show() 使您的应用程序崩溃,因为没有活动可以为您处理 UI。因此您的应用正在从网络上提取数据,但您的活动在您获取数据之前已被销毁。
    【解决方案5】:

    我正在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();
    

    我正在完成 Home Activity 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();
    

    【讨论】:

      【解决方案6】:

      我试试这个解决了。

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

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

        然后我也切换到移交 Activity 并在对话框创建中使用它的上下文 -> 现在一切正常。

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

        我,无法正确格式化代码 sn-p,抱歉 :(

        【讨论】:

          【解决方案8】:

          我收到了这个错误,但我的错误来自 Toasts,而不是对话框。

          我的布局中有 Activity 和 Fragments。 Toast 的代码在 Activity 类中。 Fragments 在 Activity 之前加载。

          我认为 Toast 代码是在 Context/Activity 完成初始化之前命中的。我认为是命令Toast.makeText(getApplicationContext(), "onMenutItemActionCollapse called", Toast.LENGTH_SHORT).show();中的getApplicationContext()

          【讨论】:

          • 你能解释一下这个:“上下文/活动完成初始化”,你到底在哪里叫 toast?我的意思是在哪种活动方法中?
          • 我在 Context 加载完成之前调用了 Toast。我猜是因为片段在活动之前被加载,所以我在片段中调用了 Toast。上下文在 Activity 中加载。
          【解决方案9】:

          试试这个:

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

          【讨论】:

            【解决方案10】:

            有了这个全局变量的想法, 我将 MainActivity 实例保存在 onCreate(); 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. 从线程打开对话框 -> 工作。
            4. 单击“返回按钮”(将调用 onCreate 并删除第一个 MainActivity)
            5. 新的 MainActivity 将启动。 (并将其实例保存到全局变量)
            6. 从第一个线程打开对话框 --> 它将打开并工作。

            :)

            【讨论】:

            • 永远不要保留对 Activity 的静态引用。会导致内存泄漏
            猜你喜欢
            • 1970-01-01
            • 2018-07-09
            • 1970-01-01
            • 2013-08-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-02-07
            相关资源
            最近更新 更多