129

从我的 mainactivity中,我需要调用一个内部类,并且在类中的一个方法中,我需要显示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();
}
4

10 回答 10

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

问题 :

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

如果您尝试从后台线程(通常从 AsyncTask 的 onPostExecute())修改 UI,并且如果活动进入完成阶段,即)显式调用完成(),用户按下主页或后退按钮或 Android 进行的活动清理,那么您得到这个错误。

原因 :

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

解决方案:

使用isFinishing()Android 调用的方法来检查该活动是否处于结束过程中:无论是显式的 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 会创建一种特殊的窗口令牌,称为应用程序窗口令牌,它唯一地标识应用程序的顶级容器窗口。活动管理器将此令牌同时提供给应用程序和窗口管理器,并且应用程序每次想在屏幕上添加新窗口时都将令牌发送给窗口管理器。这确保了应用程序和窗口管理器之间的安全交互(通过使其不可能在其他应用程序之上添加窗口),并且还使活动管理器可以轻松地直接向窗口管理器发出请求。

于 2013-09-06T20:13:27.750 回答
31

我有对话框显示功能:

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

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

if(!isFinishing())
    showDialog();
于 2015-08-13T11:26:45.403 回答
9

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

例如

而不是这个。

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

尝试使用

AlertDialog alertDialog = new AlertDialog.Builder(FirstActivity.getInstance()).create();
于 2015-01-02T05:14:17.580 回答
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();
    }
    
于 2013-09-06T16:51:09.453 回答
1

我正在创建 DialogonCreate并将其与showand一起使用hide。对我来说,根本原因不是解雇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();
于 2014-03-11T06:38:21.530 回答
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();
于 2019-08-23T15:15:26.730 回答
0

就我而言,我重构了代码并将对话框的创建放在一个单独的类中。我只交出了单击的视图,因为视图已经包含一个上下文对象。尽管都在 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))
...
}

我,无法正确格式化代码片段,抱歉:(

于 2020-08-31T09:05:37.367 回答
0

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

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

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

于 2020-09-09T15:22:47.060 回答
-1

试试这个 :

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

}
}
于 2015-03-05T10:15:24.827 回答
-3

有了这个全局变量的想法,我将 MainActivity 实例保存在 onCreate(); 安卓全局变量

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 将启动。(并将其实例保存到 globals )
  6. 从第一个线程打开对话框->它将打开并工作。

:)

于 2016-01-20T04:22:51.337 回答