【问题标题】:java.lang.IllegalArgumentException: View not attached to window managerjava.lang.IllegalArgumentException:视图未附加到窗口管理器
【发布时间】:2010-04-30 14:15:23
【问题描述】:

我有一个启动 AsyncTask 并在操作期间显示进度对话框的活动。声明该活动不会通过旋转或键盘滑动重新创建。

    <activity android:name=".MyActivity" 
              android:label="@string/app_name"
              android:configChanges="keyboardHidden|orientation"
              >
        <intent-filter>
        </intent-filter>
    </activity>

任务完成后,我会关闭对话框,但在某些手机(框架:1.5、1.6)上会抛出此类错误:

java.lang.IllegalArgumentException: View not attached to window manager
    at android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:356)
    at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:201)
    at android.view.Window$LocalWindowManager.removeView(Window.java:400)
    at android.app.Dialog.dismissDialog(Dialog.java:268)
    at android.app.Dialog.access$000(Dialog.java:69)
    at android.app.Dialog$1.run(Dialog.java:103)
    at android.app.Dialog.dismiss(Dialog.java:252)
    at xxx.onPostExecute(xxx$1.java:xxx)

我的代码是:

final Dialog dialog = new AlertDialog.Builder(context)
    .setTitle("Processing...")
    .setCancelable(true)
    .create();

final AsyncTask<MyParams, Object, MyResult> task = new AsyncTask<MyParams, Object, MyResult>() {

    @Override
    protected MyResult doInBackground(MyParams... params) {
        // Long operation goes here
    }

    @Override
    protected void onPostExecute(MyResult result) {
        dialog.dismiss();
        onCompletion(result);
    }
};

task.execute(...);

dialog.setOnCancelListener(new OnCancelListener() {
    @Override
    public void onCancel(DialogInterface arg0) {
        task.cancel(false);
    }
});

dialog.show();

根据我阅读的内容 (http://bend-ing.blogspot.com/2008/11/properly-handle-progress-dialog-in.html) 和在 Android 资源中看到的内容,似乎唯一可能出现该异常的情况是活动被破坏时。但正如我所提到的,我禁止为基本事件进行活动娱乐。

所以任何建议都非常感谢。

【问题讨论】:

  • 这个问题有很多答案,如果哪一个对你有帮助,请选择它作为正确答案。

标签: android


【解决方案1】:

当我从 onPostExecute 方法关闭对话框并完成活动时,有时也会收到此错误。我想有时活动会在对话框成功关闭之前完成。

适合我的简单而有效的解决方案

@Override
protected void onPostExecute(MyResult result) {
    try {
        if ((this.mDialog != null) && this.mDialog.isShowing()) {
            this.mDialog.dismiss();
        }
    } catch (final IllegalArgumentException e) {
        // Handle or log or ignore
    } catch (final Exception e) {
        // Handle or log or ignore
    } finally {
        this.mDialog = null;
    }  
}

【讨论】:

  • 简单的解决方案?是的。有效的?也许在这种情况下。我会推荐它吗?不!不要吞下 ALL 这样的异常!我什至不会捕获 IllegalArgumentException,而是寻找另一种解决方案。
  • 因为通常空的 try-catch 是一个坏主意......虽然它有时可能是正确的做法。
  • @Damjan 根据您的回复,您建议捕获类型异常。好吧,这是谷歌的一个不好的做法。你可以在这里阅读:Don't Catch Generic Exception.
  • 我相信这是一个有效的解决方法。在一般情况下,我们不应该这样做,但由于 Android Framework 没有为我们提供任何简单的检查,我们不得不使用不寻常的方式。此外,如果对话框的 isShowing() 调用按预期工作,我们不需要这种 hack。
  • 快速修复,直到找到更好的东西
【解决方案2】:

这是我的“防弹”解决方案,它汇集了我在这个主题上找到的所有好的答案(感谢@Damjan 和@Kachi)。仅当所有其他检测方式均未成功时,才吞下异常。在我的情况下,我需要自动关闭对话框,这是保护应用程序免于崩溃的唯一方法。 我希望它会帮助你! 如果您有意见或更好的解决方案,请投票并离开 cmets。谢谢!

public void dismissWithCheck(Dialog dialog) {
        if (dialog != null) {
            if (dialog.isShowing()) {

                //get the Context object that was used to great the dialog
                Context context = ((ContextWrapper) dialog.getContext()).getBaseContext();

                // if the Context used here was an activity AND it hasn't been finished or destroyed
                // then dismiss it
                if (context instanceof Activity) {

                    // Api >=17
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                        if (!((Activity) context).isFinishing() && !((Activity) context).isDestroyed()) {
                            dismissWithTryCatch(dialog);
                        }
                    } else {

                        // Api < 17. Unfortunately cannot check for isDestroyed()
                        if (!((Activity) context).isFinishing()) {
                            dismissWithTryCatch(dialog);
                        }
                    }
                } else
                    // if the Context used wasn't an Activity, then dismiss it too
                    dismissWithTryCatch(dialog);
            }
            dialog = null;
        }
    }

    public void dismissWithTryCatch(Dialog dialog) {
        try {
            dialog.dismiss();
        } catch (final IllegalArgumentException e) {
            // Do nothing.
        } catch (final Exception e) {
            // Do nothing.
        } finally {
            dialog = null;
        }
    }

【讨论】:

  • 非常好的解决方案!设置dialog = null 没有任何效果。 StatusEventDialog 应该只是 Dialog
  • StatusEventDialog 应改为对话框。
  • 这个答案应该是公认的,处理得很好
  • 我了解您想要“正确”并且仅在 isDestroyed() 不可用的情况下使用 try/catch,但出于实际目的,仅使用 try/catch 会不会相同总是?
【解决方案3】:

我可能有一个解决方法。

遇到了同样的问题,我通过AsyncTask 将大量项目(通过文件系统)加载到ListView 中。让onPreExecute() 启动ProgressDialog,然后onPostExecute()onCancelled()(通过AsyncTask.cancel() 显式取消任务时调用)通过.cancel() 关闭它。

当我在AsyncTaskonCancelled() 方法中杀死对话框时,得到了相同的“java.lang.IllegalArgumentException:视图未附加到窗口管理器”错误(我在优秀的@987654321 中看到了这一点) @)。

解决方法是在 AsyncTask 中创建一个包含 ProgressDialog 的公共字段:

public ProgressDialog mDialog;

然后,在onDestroy() 中,当我取消我的AsyncTask 时,我还可以通过以下方式终止关联的对话框:

AsyncTask.mDialog.cancel();

调用AsyncTask.cancel() 确实会在AsyncTask 中触发onCancelled(),但由于某种原因,在调用该方法时,视图已经被销毁,因此取消对话框失败。

【讨论】:

【解决方案4】:

以下是解决此问题的正确方法:

public void hideProgress() {
    if(mProgressDialog != null) {
        if(mProgressDialog.isShowing()) { //check if dialog is showing.

            //get the Context object that was used to great the dialog
            Context context = ((ContextWrapper)mProgressDialog.getContext()).getBaseContext();

            //if the Context used here was an activity AND it hasn't been finished or destroyed
            //then dismiss it
            if(context instanceof Activity) { 
                if(!((Activity)context).isFinishing() && !((Activity)context).isDestroyed()) 
                    mProgressDialog.dismiss();
            } else //if the Context used wasnt an Activity, then dismiss it too
                mProgressDialog.dismiss();
        }
        mProgressDialog = null;
    }
}

这个解决方案不是盲目地捕获所有异常,而是解决了问题的根源:当用于初始化对话框的活动已经完成时,试图关闭对话框。在我的 Nexus 4 上运行 KitKat,但应该适用于所有版本的 Android。

【讨论】:

  • isDestroyed 需要 API 17+
  • 为什么需要将mProgressDialog设置为null?这与内存泄漏有关吗?你能解释一下吗?
  • @Pawan,这是我的实现细节。这不是必需的,这只是此类中的函数的工作方式。隐藏进度对话框后,我将其设置为空。当用户想要显示另一个进度对话框时,会实例化一个新实例。
  • 绝对!((Activity)context).isFinishing() 是必需的,谢谢! :)
【解决方案5】:

我同意“Damjan”的观点。
如果您使用许多对话框,应关闭 onDestroy() 或 onStop() 中的所有对话框。
那么你也许可以减少“java.lang.IllegalArgumentException: View not attach to window manager”异常发生的频率。

@Override
protected void onDestroy() {
    Log.d(TAG, "called onDestroy");
    mDialog.dismiss();
    super.onDestroy();
}



但很少超过...
为了更清楚起见,您可以防止在调用 onDestroy 后显示任何对话框。
我不使用如下。但很清楚。

private boolean mIsDestroyed = false;

private void showDialog() {
    closeDialog();

    if (mIsDestroyed) {
        Log.d(TAG, "called onDestroy() already.");
        return;
    }

    mDialog = new AlertDialog(this)
        .setTitle("title")
        .setMessage("This is DialogTest")
        .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        })
        .create();
    mDialog.show();
}

private void closeDialog() {
    if (mDialog != null) {
        mDialog.dismiss();
    }
}

@Override
protected void onDestroy() {
    Log.d(TAG, "called onDestroy");
    mIsDestroyed = true;
    closeDialog();
    super.onDestroy();
}


祝你好运!

【讨论】:

  • 我总是喜欢避免空的 catch 块。值得一试,但由于这个错误很难产生 - 只有时间才能证明它是否真的有效。无论如何,谢谢。
  • 什么是空捕获块?我不使用 try/catch。一个 mIsDestroyed 变量超出了工作范围。但是,如果您在其他线程中工作后编写该对话框显示的代码,则可能需要此变量。当其他线程正在工作时,如果活动完成,您可以查看此异常。
  • 我有同样的问题,当我添加 @Override public void onPause(){ if(dialog != null) dialog.dismiss();超级.onPause(); } 直到现在我没有这个错误......所以我认为它和你的答案一样,它真的很有用
  • @ChrisSim 你好! onPuase() 和 onDestroy() 是不同的。当 Activity 为 onPuase 时,Dialog 关闭。并且当您执行应用程序时,不会显示对话框。你想要吗?
  • @Hogun 是的,当然,我的意思是一样的,我在暂停时关闭对话框,而不是在销毁时关闭对话框,因为我需要在暂停时而不是在销毁时关闭它。其次,我只在它不为空时关闭它。感谢您为其他人解释这一点。
【解决方案6】:

使用这个。

if(_dialog!=null && _dialog.isShowing())
_dialog.dismiss();

【讨论】:

  • 这与@Damjan 提出的解决方案几乎相同。
  • 这还不够,IllegalArgumentException 仍然发生在这个检查中。
  • 我做了同样的解决方案,但仍然不知道它是否有效。唯一的区别是,我嵌套了两个 if 以确保第二部分 .isShowing() 如果为 null 则不会被评估。
  • 这还不够。
  • @Nick:在这种情况下不需要嵌套多个if,Java 的&amp;&amp; 运算符具有惰性求值(也称为短路),这意味着第二个操作数是如果第一个评估为 false,则不评估(这意味着 &amp;&amp; 的结果无论如何都将始终为 false,因此是“惰性”评估)。同样,如果第一个操作数的计算结果为 true|| 将不会计算其第二个操作数。注意:&amp;| 运算符没有这种行为,因此总是计算两个操作数。
【解决方案7】:

我遇到了同样的问题,你可以通过以下方式解决:

@Override
protected void onPostExecute(MyResult result) {
    try {
        if ((this.mDialog != null) && this.mDialog.isShowing()) {
            this.mDialog.dismiss();
        }
    } catch (final IllegalArgumentException e) {
        // Handle or log or ignore
    } catch (final Exception e) {
        // Handle or log or ignore
    } finally {
        this.mDialog = null;
    }  
}

【讨论】:

    【解决方案8】:

    我认为您的代码是正确的,与建议的其他答案不同。 onPostExecute 将在 UI 线程上运行。这就是 AsyncTask 的全部意义——您不必担心调用 runOnUiThread 或处理处理程序。此外,根据文档,可以从任何线程安全地调用 dismiss()(不确定他们是否将此作为例外)。

    也许是在 Activity 不再显示后调用 dialog.dismiss() 的时间问题?

    如果您注释掉 setOnCancelListener 然后在后台任务运行时退出活动,测试会发生什么情况如何?然后您的 onPostExecute 将尝试关闭已关闭的对话框。如果应用程序崩溃,您可以在关闭对话框之前检查对话框是否打开。

    我遇到了完全相同的问题,所以我将在代码中尝试一下。

    【讨论】:

    • 我还研究了 dimiss() 代码,确实可以从任何线程安全地调用它。顺便说一句,我在测试时遇到了问题,因为这个问题发生在用户手机上,而且我自己无法重现:-(所以试图通过分析代码来弄清楚......根据时间安排。我在想这个,但无法想象Activity如何在Dialog之前关闭的情况。如果按下BACK,它将首先取消Dialog。并且清单文件禁止Activity自动重新创建,但可能它仍然可以重新创建一些如何?让我知道你是否找到了一些东西!
    【解决方案9】:

    亚历克斯,

    我在这里可能是错的,但我怀疑“野外”的多部手机存在一个错误,导致它们在标记为静态定向的应用程序上切换方向。这种情况经常发生在我的个人手机上,以及我们小组使用的许多测试手机上(包括 droid、n1、g1、hero)。通常,标记为静态定向(可能是垂直)的应用程序会使用水平方向将自己放置一两秒钟,然后立即切换回来。最终结果是,即使您不希望您的应用程序切换方向,您也必须做好准备。我不知道在什么确切条件下可以重现这种行为,我不知道它是否特定于某个 Android 版本。我所知道的是我已经看到它发生了很多次:(

    我建议使用in the link you posted 提供的解决方案,该解决方案建议覆盖 Activity onCreateDialog 方法并让 Android 操作系统管理对话框的生命周期。在我看来,即使您不希望您的活动切换方向,它也在某处切换方向。您可以尝试寻找一种始终阻止方向切换的方法,但我想告诉您,我个人不相信有一种万无一失的方法适用于市场上所有当前的 Android 手机。

    【讨论】:

    • 您可以防止设备切换方向,但还有许多其他配置更改会破坏/重新创建您的 Activity - 一个常见的更改是滑入或滑出键盘。
    【解决方案10】:

    大多数时候对我有用的是验证 Activity 是否没有完成。

    if (!mActivity.isFinishing()) {
        dialog.dismiss();
    }
    

    【讨论】:

      【解决方案11】:

      声明该活动不会通过旋转或键盘滑动重新创建。

      刚刚遇到同样的问题。修复 API 级别 13 或更高级别。
      来自 Android 文档:

      注意:如果您的应用程序以 API 级别 13 或更高级别为目标(由 minSdkVersion 和 targetSdkVersion 属性声明),那么您还应该声明“screenSize”配置,因为当设备在纵向和横向之间切换时它也会改变。

      所以我已将清单更改为:

      <activity
              android:name="MyActivity"
              android:configChanges="orientation|screenSize"
              android:label="MyActivityName" >
      </activity>
      

      现在它工作正常。当我旋转手机时,活动没有重新创建,进度对话框和视图保持不变。对我来说没有错误。

      【讨论】:

      • 这不是解决方案。例如,我有来自 admob 的 Adview 活动,它应该为大小更改重新创建活动。
      【解决方案12】:

      首先在您试图关闭对话框的地方进行错误处理。

       if ((progressDialog != null) && progressDialog.isShowing()) {
                  progressDialog.dismiss();
                  progressDialog = null;
              }
      

      如果这不能解决,则在活动的 onStop() 方法中将其关闭。

       @Override
          protected void onStop() {
              super.onStop();
              if ((progressDialog != null) && progressDialog.isShowing()) {
                  progressDialog.dismiss();
                  progressDialog = null;
              }
          }
      

      【讨论】:

        【解决方案13】:

        我在使用按钮从服务器同步列表时遇到了同样的问题: 1)我点击按钮 2) 从服务器下载列表时会出现一个进度对话框 3) 我将设备转到另一个方向 4) java.lang.IllegalArgumentException: 在progress.dismiss() 期间,视图未附加到AsyncTask 的postExecute() 上的窗口管理器。

        当我尝试修复时,我发现即使问题没有发生,我的列表也没有显示所有项目。

        我想我想要的是让 AsyncTask 在 Activity 被销毁之前完成(并关闭对话框),所以我将 asynctask 对象设置为一个属性并覆盖了 onDestroy() 方法。

        如果异步任务花费了很多时间,用户可能会觉得设备很慢,但我认为这是他在显示进度对话框时尝试更改设备方向所付出的代价。即使需要一些时间,应用程序也不会崩溃。

        private AsyncTask<Boolean, Void, Boolean> atask;
        
        @Override
        protected void onDestroy() {
            if (atask!=null)
                try {
                    atask.get();
                } catch (InterruptedException e) {
                } catch (ExecutionException e) {
                }
            super.onDestroy();
        }
        

        【讨论】:

          【解决方案14】:
          @Override
                  protected void onPostExecute(Void result) {
                      super.onPostExecute(result);
          
                      if (progressDialog != null && progressDialog.isShowing()) {
                          Log.i(TAG, "onPostexucte");
                          progressDialog.dismiss();
          }
          }
          

          【讨论】:

          • 虽然此代码 sn-p 可能会回答问题,并提供一些关于它如何解决问题的解释,但将有助于该网站的未来访问者理解您的答案
          【解决方案15】:

          下面的代码对你有用,对我来说非常好:

          private void viewDialog() {
              try {
                  Intent vpnIntent = new Intent(context, UtilityVpnService.class);
                  context.startService(vpnIntent);
                  final View Dialogview = View.inflate(getBaseContext(), R.layout.alert_open_internet, null);
                  final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                          WindowManager.LayoutParams.WRAP_CONTENT,
                          WindowManager.LayoutParams.WRAP_CONTENT,
                          WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                          WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                                  | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_DIM_BEHIND,
                          PixelFormat.TRANSLUCENT);
                  params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL;
                  windowManager.addView(Dialogview, params);
          
                  Button btn_cancel = (Button) Dialogview.findViewById(R.id.btn_canceldialog_internetblocked);
                  Button btn_okay = (Button) Dialogview.findViewById(R.id.btn_openmainactivity);
                  RelativeLayout relativeLayout = (RelativeLayout) Dialogview.findViewById(R.id.rellayout_dialog);
          
                      btn_cancel.setOnClickListener(new View.OnClickListener() {
                          @Override
                          public void onClick(View view) {
                              Handler handler = new Handler(Looper.getMainLooper());
                              handler.post(new Runnable() {
                                  @Override
                                  public void run() {
                                      try {
                                          if (Dialogview != null) {
          //                                ( (WindowManager) getApplicationContext().getSystemService(WINDOW_SERVICE)).removeView(Dialogview);
                                              windowManager.removeView(Dialogview);
                                          }
                                      } catch (final IllegalArgumentException e) {
                                          e.printStackTrace();
                                          // Handle or log or ignore
                                      } catch (final Exception e) {
                                          e.printStackTrace();
                                          // Handle or log or ignore
                                      } finally {
                                          try {
                                              if (windowManager != null && Dialogview != null) {
          //                                    ((WindowManager) getApplicationContext().getSystemService(WINDOW_SERVICE)).removeView(Dialogview);
                                                  windowManager.removeView(Dialogview);
                                              }
                                          } catch (Exception e) {
                                              e.printStackTrace();
                                          }
                                      }
                                      //    ((WindowManager) getApplicationContext().getSystemService(WINDOW_SERVICE)).removeView(Dialogview);
          //                        windowManager.removeView(Dialogview);
          
          
                                  }
                              });
                          }
                      });
                      btn_okay.setOnClickListener(new View.OnClickListener() {
                          @Override
                          public void onClick(View view) {
                              Handler handler = new Handler(Looper.getMainLooper());
                              handler.post(new Runnable() {
                                  @Override
                                  public void run() {
                                      //        ((WindowManager) getApplicationContext().getSystemService(WINDOW_SERVICE)).removeView(Dialogview);
                                      try {
                                          if (windowManager != null && Dialogview != null)
                                              windowManager.removeView(Dialogview);
                                          Intent intent = new Intent(getBaseContext(), SplashActivity.class);
                                          intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          //                        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
          //                        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
          
          
                                          context.startActivity(intent);
                                      } catch (Exception e) {
                                          windowManager.removeView(Dialogview);
                                          e.printStackTrace();
                                      }
                                  }
                              });
                          }
                      });
                  } catch (Exception e) {
                      //` windowManager.removeView(Dialogview);
                      e.printStackTrace();
                  }
              }
          

          如果您从后台服务调用它,请不要全局定义您的视图。

          【讨论】:

            猜你喜欢
            • 2012-06-09
            • 2011-03-23
            • 2014-05-20
            • 1970-01-01
            • 1970-01-01
            • 2013-11-01
            • 1970-01-01
            • 2011-01-14
            • 1970-01-01
            相关资源
            最近更新 更多