首页 文章

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

提问于
浏览
144

我有一个启动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 回答

  • 223
    @Override
            protected void onPostExecute(Void result) {
                super.onPostExecute(result);
    
                if (progressDialog != null && progressDialog.isShowing()) {
                    Log.i(TAG, "onPostexucte");
                    progressDialog.dismiss();
    }
    }
    
  • 2

    当我从onPostExecute方法中解除对话并完成活动时,我也得到此错误 sometimes . 我猜有时活动会在对话成功解散之前完成 .

    简单而有效的解决方案对我有用

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

    我可能有一个解决方法 .

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

    当我在 AsyncTaskonCancelled() 方法中杀死对话框时出现了相同的"java.lang.IllegalArgumentException: View not attached to window manager"错误(我在优秀的Shelves app中看到了这一点) .

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

    public ProgressDialog mDialog;
    

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

    AsyncTask.mDialog.cancel();
    

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

  • 11

    这是我的“防弹”解决方案,它汇集了我在这个主题上找到的所有好答案(感谢@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;
            }
        }
    
  • 5

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

    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 .

  • 1

    我同意'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();
    }
    

    祝好运!

  • 2

    用这个 .

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

  • 9

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

    @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

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

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

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

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

  • 2

    亚历克斯,

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

    我建议使用提供的解决方案in the link you posted建议覆盖Activity onCreateDialog方法并让Android OS管理Dialogs的生命周期 . 它看起来像我,即使你不相信有一种万无一失的方式适用于市场上所有现有的Android手机 .

  • 2

    大多数时候对我有用的是验证活动是否没有完成 .

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

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

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

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

    所以我改变了我的清单:

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

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

  • 4

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

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

    使用按钮从服务器同步列表时遇到了同样的问题: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();
    }
    
  • 3

    下面的代码适合你,它对我很有用:

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

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

相关问题