ChatGPT解决这个技术问题 Extra ChatGPT

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

从我的主 activity,我需要调用一个内部类,并且在类内的一个方法中,我需要显示 AlertDialog。关闭后,当按下 OK 按钮时,转发到 Google Play 进行购买。

在大多数情况下,事情都很顺利,但对于少数用户来说,它在 builder.show() 上崩溃了,我可以从崩溃日志中看到 "android.view.WindowManager$BadTokenException: Unable to add window"。请提出建议。

我的代码几乎是这样的:

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

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

问题 :

当应用程序尝试通过打开对话框从后台线程 (AsyncTask) 通知用户时,会发生此异常。如果您尝试从后台线程(通常从 AsyncTask 的 onPostExecute())修改 UI,并且如果活动进入完成阶段,即)显式调用完成(),用户按下主页或后退按钮或 Android 进行的活动清理,那么您得到这个错误。

原因 :

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

解决方案:

使用 Android 调用的 isFinishing() 方法来检查该活动是否处于完成过程中:无论是显式的 finish() 调用还是 Android 进行的活动清理。通过使用这种方法,很容易避免在活动完成时从后台线程打开对话框。还要为活动维护一个弱引用(而不是强引用,以便在不需要时可以销毁活动)并在使用此活动引用执行任何 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)
'空白最终字段 loginActivityWeakRef 可能尚未初始化' 消息并以这种方式尝试: private final WeakReference loginActivityWeakRef = new WeakReference(login.this);不确定这是否正确
尝试使用 new chkCubscription(this).execute("");而不是 new chkCubscription.execute("");正如您在上面发布的那样。
它也可以是吐司。
可怕的错误!!我正在学习一个教程,作为@PhilRoggenbuck,我的问题是由在调用 StartActivity(...) 之前调用 Toast..Show() 引起的。为了解决这个问题,我将 toast 移到了新调用的活动中!
J
Jemshit Iskenderov

我有对话框显示功能:

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

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

if(!isFinishing())
    showDialog();

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

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

例如

而不是这个。

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

尝试使用

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

T
Tony_A

首先,您不能在不覆盖 doInBackground 的情况下扩展 AsyncTask

第二次尝试从构建器创建 AlterDailog,然后调用 show()。私人布尔可见=假;类 chkSubscription 扩展 AsyncTask { 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();如果(可见) myAlertDialog.show(); } @Override protected String doInBackground(String... arg0) { // TODO 自动生成的方法存根 return null; } } @Override protected void onResume() { // TODO 自动生成的方法存根 super.onResume();可见=真; } @Override 受保护的 void onStop() { 可见 = false;超级.onStop(); }


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

我在 onCreate 中创建 Dialog 并将其与 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();

h
himanshu kumar

我试试这个它解决了。

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

E
Emanuel

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

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


G
Gene

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

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

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


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

尝试这个 :

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

}
}

C
Community

有了这个全局变量的想法,我将 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);

打开 MainActivity 启动一个线程。从线程打开对话框-> 工作。单击“返回按钮”(将调用 onCreate 并删除第一个 MainActivity)新的 MainActivity 将启动。 (并将其实例保存到全局变量)从第一个线程打开对话框->它将打开并工作。

:)


永远不要保留对 Activity 的静态引用。会导致内存泄漏