java.lang.IllegalArgumentException:视图未附加到窗口管理器

ale*_*2k8 146 android

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

    <activity android:name=".MyActivity" 
              android:label="@string/app_name"
              android:configChanges="keyboardHidden|orientation"
              >
        <intent-filter>
        </intent-filter>
    </activity>
Run Code Online (Sandbox Code Playgroud)

任务完成后,我会忽略对话框,但在某些手机上(框架: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)
Run Code Online (Sandbox Code Playgroud)

我的代码是:

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();
Run Code Online (Sandbox Code Playgroud)

从我已阅读(http://bend-ing.blogspot.com/2008/11/properly-handle-progress-dialog-in.html)和Android的源看到的,它看起来像唯一可能的情况下,以获取例外是活动被销毁的时候.但正如我所提到的,我禁止基本活动的活动娱乐.

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

Dam*_*jan 226

当我从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;
    }  
}
Run Code Online (Sandbox Code Playgroud)

  • 简单的方案?是.有效?也许在这种情况下.我会推荐它吗?没有!不要吞下**所有**例外!我甚至不会捕获IllegalArgumentException,但寻找另一种解决方案. (44认同)
  • 我相信这是一个有效的解决方案 在一般情况下,我们不应该这样做,但由于Android框架不能为我们提供任何简单的检查,我们必须使用不寻常的方式.此外,如果对话框的isShowing()调用按预期工作,我们不需要这种黑客攻击. (17认同)
  • 因为通常空的尝试捕获是一个坏主意......虽然它有时可能是正确的事情. (6认同)
  • @Damjan根据您的回复,您建议捕获类型异常.嗯,谷歌这是一个不好的做法.你可以在这里阅读:[不要捕获通用例外](http://source.android.com/source/code-style.html#dont-catch-generic-exception). (3认同)

Ivo*_*nov 12

这是我的"防弹"解决方案,它汇集了我在这个主题上找到的所有好答案(感谢@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;
        }
    }
Run Code Online (Sandbox Code Playgroud)


Pau*_*ega 11

我可能有一个解决方法.

有同样的问题,我正在加载很多项目(通过文件系统)到一个ListView通过AsyncTask.如果onPreExecute()激活a ProgressDialog,然后两者onPostExecute()onCancelled()(当任务被明确取消时调用AsyncTask.cancel())通过关闭它.cancel().

得到相同的"java.lang.IllegalArgumentException:视图没有附加到窗口管理器"错误当我在onCancelled()方法中杀死对话框AsyncTask(我在优秀的Shelves应用程序中看到了这一点).

解决方法是创建一个AsyncTask包含以下内容的公共字段ProgressDialog:

public ProgressDialog mDialog;
Run Code Online (Sandbox Code Playgroud)

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

AsyncTask.mDialog.cancel();
Run Code Online (Sandbox Code Playgroud)

呼叫AsyncTask.cancel()DOES触发onCancelled()AsyncTask,但由于某些原因通过该方法被调用的时候,查看已经被销毁,因此撤销对话框失败.


Kac*_*chi 9

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • `isDestroyed`需要API 17+ (3认同)

Hog*_*gun 5

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

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



但是
,为了使它更清晰,你可以防止在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();
}
Run Code Online (Sandbox Code Playgroud)


祝好运!


Bra*_*rke 2

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

也许这是一个计时问题,在活动不再显示后调用dialog.dismiss()?

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

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