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

在我的主 activity中,我需要调用一个内部类,在类中的一个方法中,我需要显示 AlertDialog。解雇后,当确定按钮被按下,转发到谷歌播放购买。

在大多数情况下,一切都很完美,但是对于少数用户来说,它在 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();
}
142282 次浏览
  • 首先,如果不重写 doInBack,就不能扩展 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();
    }
    
android.view.WindowManager$BadTokenException: Unable to add window"

问题:

此异常发生在应用程序试图通知用户 通过打开一个对话框来访问后台线程(AsyncTask)。

如果您试图从后台线程修改 UI (通常 从 AsyncTask 的 onPostExecute ()) ,并且如果活动进入 完成阶段,即)显式调用 Finish () ,用户按下 home 键 或者后退按钮,或者 Android 制作的清理活动,然后你就会看到这个 错误。

理由:

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

解决方案:

使用由 Android 调用的 isFinishing()方法检查 这个活动正在完成的过程中: 可以是明确的完成() 调用或活动清理由 Android。通过使用这种方法 活动时很容易避免从后台线程打开对话框 就要结束了。

也保持一个 weak reference的活动(而不是一个强大的 引用,以便在不需要的时候可以销毁活动)并检查 如果活动没有完成,则在使用此 活动引用(即显示一个对话框)。

例如:。

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创建一种特殊的窗口标记 称为应用程序窗口标记,该标记唯一标识 应用程序的顶级容器窗口 将此标记传递给应用程序和窗口管理器,并将 应用程序每次需要时都将令牌发送给窗口管理器 在屏幕上添加一个新窗口。这样可以确保安全的交互 在应用程序和窗口管理器之间(通过 不可能在其他应用程序之上添加窗口) ,以及 使得活动管理器很容易直接向 窗口管理器。

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

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

例如:

而不是这样。

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

尝试使用

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

试试这个:

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


}
}

我有对话框显示功能:

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

我得到这个错误,我只需要检查 isFinishing()之前调用这个对话框显示功能。

if(!isFinishing())
showDialog();

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

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. 开放的主要活动
  2. 开始穿线。
  3. 从 thread-> work 打开对话框。
  4. 单击“ Back 按钮”(将调用 onCreate 并删除第一个 MainActivity)
  5. 新的 MainActivity 将启动(并将其实例保存到全局)
  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();

在我的例子中,我重构了代码,并将 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))
...
}

我,无法正确格式化代码片段,对不起: (

我得到了这个错误,但我的是来自祝酒词,而不是对话。

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

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