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

时间:2010-04-30 14:15:23

标签: android

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

所以非常感谢任何建议。

15 个答案:

答案 0 :(得分:224)

当我解除对话并从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;
    }  
}

答案 1 :(得分:11)

我可能有一个解决方法。

遇到了同样的问题,我通过ListView将大量项目(通过文件系统)加载到AsyncTask。如果onPreExecute()启动ProgressDialog,然后启用onPostExecute()onCancelled()(通过AsyncTask.cancel()明确取消任务时调用),请通过{{{ 1}}。

当我在.cancel()的{​​{1}}方法中杀死对话框时,得到了相同的“java.lang.IllegalArgumentException:视图未附加到窗口管理器”错误(我看到这样做了优秀的Shelves app)。

解决方法是在onCancelled()中创建包含AsyncTask的公共字段:

AsyncTask

然后,在ProgressDialog取消public ProgressDialog mDialog; 时,我还可以通过以下方式终止相关对话框:

onDestroy()

AsyncTask中调用AsyncTask.mDialog.cancel(); DOES触发AsyncTask.cancel(),但由于某种原因,在调用该方法时,View已被销毁,因此取消对话框失败。

答案 2 :(得分:10)

这是我的&#34;防弹&#34;解决方案,这是我在这个主题上找到的所有好答案的汇编(感谢@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;
        }
    }

答案 3 :(得分:8)

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

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

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

答案 4 :(得分: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();
}



但是很少超过...... 为了使它更清晰,你可以阻止在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();
}


祝你好运!

答案 5 :(得分:4)

使用此功能。

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

答案 6 :(得分:3)

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

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

答案 7 :(得分:2)

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

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

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

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

答案 8 :(得分:2)

alex,

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

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

答案 9 :(得分:2)

大部分时间对我有用的是验证活动是否没有完成。

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

答案 10 :(得分:2)

  

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

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

  

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

所以我改变了我的清单:

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

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

答案 11 :(得分:2)

首先,在尝试关闭对话框时进行错误处理。

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

答案 12 :(得分:1)

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

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

我认为我想要的是AsyncTask在活动beign销毁之前完成(并关闭对话框),所以我将asynctask对象作为属性并覆盖了onDestroy()方法。

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

private AsyncTask<Boolean, Void, Boolean> atask;

@Override
protected void onDestroy() {
    if (atask!=null)
        try {
            atask.get();
        } catch (InterruptedException e) {
        } catch (ExecutionException e) {
        }
    super.onDestroy();
}

答案 13 :(得分:1)

@Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);

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

答案 14 :(得分:0)

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

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

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