异常: 未附加到窗口管理器的视图

我有一个活动,它启动 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 源代码中看到的情况来看,似乎获得该异常的唯一可能的情况是当活动被销毁时。但正如我提到的,我禁止基本活动的活动娱乐。

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

119312 次浏览

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

也许是因为在活动不再显示之后调用 dialog.fire ()的时间问题?

如果注释掉 setOnCancelListener,然后在后台任务运行时退出活动,那么测试会发生什么情况呢?然后 onPostExecute 将尝试取消已经被取消的对话。如果应用程序崩溃,你可能只是检查对话框是否打开,然后就可以取消它。

我遇到了完全相同的问题,所以我要用代码来试试。

亚历克斯,

这里我可能错了,但是我怀疑“在野外”的多部手机有一个 bug,导致它们在标记为静态导向的应用程序上切换方向。这种情况在我的个人手机上发生过很多次,在我们小组使用的许多测试手机上也是如此(包括 droid,n1,g1,Hero)。通常,标记为静态方向(可能是垂直方向)的应用程序会使用水平方向进行一两秒钟的展示,然后立即切换回来。最终的结果是,即使你不希望你的应用程序切换方向,你必须做好准备,它可能。我不知道在什么样的确切条件下这种行为可以被复制,我不知道它是否只适用于某个版本的 Android。我所知道的是,我已经看到这种情况发生了很多次:

我建议使用提供的 在你发布的链接中解决方案,该解决方案建议重写 Activity onCreateDialog 方法,并让 Android 操作系统管理 Dialogs 的生命周期。在我看来,即使你不想让你的活动改变方向,它也是在某个地方改变方向。你可以尝试找到一种方法来防止方向切换,但是我个人认为目前市场上所有的 Android 手机都没有一种万无一失的方法。

我可能有办法。

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

一样的“ java.lang”。IllegalArgumentException: 视图未附加到窗口管理器”错误,当我在 AsyncTaskonCancelled()方法中杀死对话框时(我在优秀的 货架应用中看到过这样做)。

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

public ProgressDialog mDialog;

然后,在 onDestroy()中,当我取消 AsyncTask时,我也可以通过以下方式杀死相关的对话框:

AsyncTask.mDialog.cancel();

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

当我关闭对话框并从 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;
}
}

用这个。

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

我同意‘ Damjan’的观点。
如果使用多个对话框,则应关闭 onDestroy ()或 onStop ()中的所有对话框。
然后你就可以减少‘ java.lang. IllegalArgumentException: View not attedto 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();
}


祝你好运!

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

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

我认为我想要的是 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();
}

对我来说,最有效的方法是验证活动是否没有完成。

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

我也有同样的问题,你可以通过:

@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;
}
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);


if (progressDialog != null && progressDialog.isShowing()) {
Log.i(TAG, "onPostexucte");
progressDialog.dismiss();
}
}

声明该活动不能通过旋转或键盘幻灯片重新创建。

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

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

所以我把我的旅客名单改成了这样:

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

现在好了。当我旋转手机,进度对话框和视图保持不变时,活动不再创建。对我来说没有错误。

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

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

该解决方案不盲目地捕捉所有异常,而是解决问题的根源: 当用于初始化对话框的活动已经完成时,尝试放弃对话框。在我的 Nexus4上运行 KitKat,但是应该适用于所有的 Android 版本。

首先,无论何时试图关闭对话框,都要进行错误处理。

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

这是我的“防弹”解决方案,它汇编了我在这个主题上找到的所有好答案(感谢@Damjan 和@Kachi)。在这里,只有在所有其他检测方法都没有成功的情况下,才会吞噬异常。在我的情况下,我需要自动关闭对话框,这是唯一的方法,以保护应用程序从崩溃。 我希望它能帮助你! 如果你有意见或更好的解决方案,请投票并留言。谢谢!

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

Migh 下面的代码对你很有用,对我很有用:

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

如果从后台服务调用,则不要全局定义视图。