代码之家  ›  专栏  ›  技术社区  ›  Daniel Benedykt

Android视图未连接到窗口管理器

  •  106
  • Daniel Benedykt  · 技术社区  · 15 年前

    我有以下一些例外情况:

    java.lang.IllegalArgumentException: View not attached to window manager
    at android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:355)
    at android.view.WindowManagerImpl.updateViewLayout(WindowManagerImpl.java:191)
    at android.view.Window$LocalWindowManager.updateViewLayout(Window.java:428)
    at android.app.Dialog.onWindowAttributesChanged(Dialog.java:596)
    at android.view.Window.setDefaultWindowFormat(Window.java:1013)
    at com.android.internal.policy.impl.PhoneWindow.access$700(PhoneWindow.java:86)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.drawableChanged(PhoneWindow.java:1951)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.fitSystemWindows(PhoneWindow.java:1889)
    at android.view.ViewRoot.performTraversals(ViewRoot.java:727)
    at android.view.ViewRoot.handleMessage(ViewRoot.java:1633)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:123)
    at android.app.ActivityThread.main(ActivityThread.java:4338)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:521)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
    at dalvik.system.NativeStart.main(Native Method)
    

    我在谷歌上搜索了一下,发现它与弹出窗口和屏幕切换有关,但没有引用我的代码。

    问题是:

    1. 有办法知道吗 这个问题的确切时间是 发生了吗?
    2. 除了转动屏幕外,是否还有其他事件或操作触发此错误?
    3. 我如何防止这种情况发生?
    13 回复  |  直到 6 年前
        1
  •  157
  •   Jared Burrows    10 年前

    我遇到了这个问题,在屏幕方向改变时,活动在异步任务完成之前完成,进度对话框完成。我似乎通过将对话框设置为空来解决这个问题。 onPause() 然后在取消前在AsyncTask中检查。

    @Override
    public void onPause() {
        super.onPause();
    
        if ((mDialog != null) && mDialog.isShowing())
            mDialog.dismiss();
        mDialog = null;
    }
    

    …在我的异步任务中:

    protected void onPreExecute() {
        mDialog = ProgressDialog.show(mContext, "", "Saving changes...",
                true);
    }
    
    protected void onPostExecute(Object result) {
       if ((mDialog != null) && mDialog.isShowing()) { 
            mDialog.dismiss();
       }
    }
    
        2
  •  8
  •   Community CDub    6 年前

    在与这个问题作了一番斗争之后,我终于找到了解决方法:

    /**
     * Dismiss {@link ProgressDialog} with check for nullability and SDK version
     *
     * @param dialog instance of {@link ProgressDialog} to dismiss
     */
    public void dismissProgressDialog(ProgressDialog dialog) {
        if (dialog != null && 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 (!((Activity) context).isFinishing() {
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                            if (!((Activity) context).isDestroyed()) {
                                dismissWithExceptionHandling(dialog);
                            }
                        } else { 
                            // Api < 17. Unfortunately cannot check for isDestroyed()
                            dismissWithExceptionHandling(dialog);
                        }
                    }
                } else
                    // if the Context used wasn't an Activity, then dismiss it too
                    dismissWithExceptionHandling(dialog);
            }
            dialog = null;
        }
    }
    
    /**
     * Dismiss {@link ProgressDialog} with try catch
     *
     * @param dialog instance of {@link ProgressDialog} to dismiss
     */
    public void dismissWithExceptionHandling(ProgressDialog dialog) {
        try {
            dialog.dismiss();
        } catch (final IllegalArgumentException e) {
            // Do nothing.
        } catch (final Exception e) {
            // Do nothing.
        } finally {
            dialog = null;
        }
    }
    

    有时,如果没有更好的解决方案来解决这个问题,好的异常处理会很好地工作。

        3
  •  4
  •   shawkinaw    9 年前

    如果你有 Activity 挂在周围的对象,可以使用 isDestroyed() 方法:

    Activity activity;
    
    // ...
    
    if (!activity.isDestroyed()) {
        // ...
    }
    

    如果你有一个非匿名的 AsyncTask 在不同的地方使用的子类。

        4
  •  2
  •   Lucifer phtrivier    12 年前

    我将以下内容添加到该活动的清单中

    android:configChanges="keyboardHidden|orientation|screenLayout"
    
        5
  •  2
  •   aimiliano    9 年前

    我使用的是一个定制的静态类,它可以显示和隐藏一个对话框。 这个类也被其他活动使用,而不仅仅是一个活动。 现在你所描述的问题也出现在我看来,我已经熬夜寻找解决方案。

    最后,我向您介绍解决方案!

    如果您想显示或关闭一个对话框,但不知道是哪个活动启动了该对话框以进行触摸,那么下面的代码是为您准备的。

     static class CustomDialog{
    
         public static void initDialog(){
             ...
             //init code
             ...
         }
    
          public static void showDialog(){
             ...
             //init code for show dialog
             ...
         }
    
         /****This is your Dismiss dialog code :D*******/
         public static void dismissProgressDialog(Context context) {                
                //Can't touch other View of other Activiy..
                //http://stackoverflow.com/questions/23458162/dismiss-progress-dialog-in-another-activity-android
                if ( (progressdialog != null) && progressdialog.isShowing()) {
    
                    //is it the same context from the caller ?
                    Log.w("ProgressDIalog dismiss", "the dialog is from"+progressdialog.getContext());
    
                    Class caller_context= context.getClass();
                    Activity call_Act = (Activity)context;
                    Class progress_context= progressdialog.getContext().getClass();
    
                    Boolean is_act= ( (progressdialog.getContext()) instanceof  Activity )?true:false;
                    Boolean is_ctw= ( (progressdialog.getContext()) instanceof  ContextThemeWrapper )?true:false;
    
                    if (is_ctw) {
                        ContextThemeWrapper cthw=(ContextThemeWrapper) progressdialog.getContext();
                        Boolean is_same_acivity_with_Caller= ((Activity)(cthw).getBaseContext() ==  call_Act )?true:false;
    
                        if (is_same_acivity_with_Caller){
                            progressdialog.dismiss();
                            progressdialog = null;
                        }
                        else {
                            Log.e("ProgressDIalog dismiss", "the dialog is NOT from the same context! Can't touch.."+((Activity)(cthw).getBaseContext()).getClass());
                            progressdialog = null;
                        }
                    }
    
    
                }
            } 
    
     }
    
        6
  •  0
  •   Steve Haley    15 年前

    对于问题1):

    考虑到错误消息似乎没有说明哪一行代码导致了问题,您可以使用断点来跟踪它。当程序到达特定的代码行时,断点暂停程序的执行。通过向关键位置添加断点,可以确定哪一行代码导致崩溃。例如,如果您的程序在setContentView()行崩溃,您可以在那里放置一个断点。当程序运行时,它将在运行该行之前暂停。如果在到达下一个断点之前恢复导致程序崩溃,那么您就知道终止程序的行在两个断点之间。

    如果使用Eclipse,添加断点很容易。右键单击代码左边的空白处,然后选择“切换断点”。然后,您需要在调试模式下运行应用程序,这个按钮看起来像是正常运行按钮旁边的绿色昆虫。当程序到达一个断点时,Eclipse将切换到Debug透视图并向您显示它正在等待的行。若要重新启动程序,请查找“恢复”按钮,该按钮看起来像正常的“播放”,但在三角形左侧有一个垂直条。

    您还可以在应用程序中填入log d.d(“我的应用程序”,“这里的一些信息告诉您日志行在哪里”),然后在Eclipse的logcat窗口中发布消息。如果找不到该窗口,请使用“窗口->显示视图->其他”打开它…->安卓->logcat。

    希望有帮助!

        7
  •  0
  •   android developer    11 年前

    根据WindowManager的代码(链接 here ,当试图更新的视图(可能属于某个对话框,但不是必需的)不再附加到窗口的实际根目录时,就会发生这种情况。

    正如其他人所建议的,在对对话框执行特殊操作之前,您应该检查活动的状态。

    以下是相关代码,这是问题的原因(从Android源代码复制):

    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }
    
        final WindowManager.LayoutParams wparams
                = (WindowManager.LayoutParams)params;
    
        view.setLayoutParams(wparams);
    
        synchronized (this) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots[index];
            mParams[index] = wparams;
            root.setLayoutParams(wparams, false);
        }
    }
    
    private int findViewLocked(View view, boolean required) {
            synchronized (this) {
                final int count = mViews != null ? mViews.length : 0;
                for (int i=0; i<count; i++) {
                    if (mViews[i] == view) {
                        return i;
                    }
                }
                if (required) {
                    throw new IllegalArgumentException(
                            "View not attached to window manager");
                }
                return -1;
            }
        }
    
        8
  •  0
  •   Roger    10 年前

    我的问题是通过在我的安卓上锁定屏幕旋转来解决的。这个应用程序导致了我的一个问题,现在工作得很好。

        9
  •  0
  •   Nick Palmer    10 年前

    另一个选项是,在通过重写对话框上的OnAttachedToWindow()将对话框附加到窗口之前,不要启动异步任务,这样它总是不可接受的。

        10
  •  0
  •   Nidhish Krishnan    8 年前

    或者你可以简单地加上

    protected void onPreExecute() {
        mDialog = ProgressDialog.show(mContext, "", "Saving changes...", true, false);
    }
    

    这将使 ProgressDialog 无法取消

        11
  •  0
  •   Kishan Solanki    7 年前

    上述解决方案对我不起作用。所以我所做的就是 ProgressDialog 作为全局设置,然后将其添加到我的活动中

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

    因此,如果活动被破坏,那么ProgressDialog也将被破坏。

        12
  •  -2
  •   Heasen    8 年前

    为什么不试着接球,就像这样:

    protected void onPostExecute(Object result) {
            try {
                if ((mDialog != null) && mDialog.isShowing()) {
                    mDialog.dismiss();
                }
            } catch (Exception ex) {
                Log.e(TAG, ex.getMessage(), ex);
            }
        }
    
        13
  •  -3
  •   Cristian    12 年前

    当您在清单中声明活动时,您需要android:configchanges=“orientation”

    例子:

    <activity android:theme="@android:style/Theme.Light.NoTitleBar" android:configChanges="orientation"  android:label="traducción" android:name=".PantallaTraductorAppActivity"></activity>