Android View не подключен к менеджеру окон

У меня есть некоторые из следующих исключений:

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)

У меня есть googled и вижу, что он имеет какое-то отношение к всплывающим окнам и поворот экрана, но ссылка на мой код отсутствует.

Вопросы:

  • Есть ли способ узнать точно, когда эта проблема происходит?
  • кроме поворота экрана, есть ли другое событие или действие, которое вызывает эту ошибку?
  • Как я могу предотвратить это?

Ответ 1

У меня была эта проблема, когда при изменении ориентации экрана активность завершалась до AsyncTask с завершением диалогового окна прогресса. Я, похоже, решил это, установив диалог null onPause(), а затем проверив это в AsyncTask перед увольнением.

@Override
public void onPause() {
    super.onPause();

    if ((mDialog != null) && mDialog.isShowing())
        mDialog.dismiss();
    mDialog = null;
}

... в моей AsyncTask:

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

protected void onPostExecute(Object result) {
   if ((mDialog != null) && mDialog.isShowing()) { 
        mDialog.dismiss();
   }
}

Ответ 2

После борьбы с этой проблемой, я, наконец, закончил с этим обходным путем:

/**
 * 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) {
        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()) {
                        dismissWithExceptionHandling(dialog);
                    }
                } else {

                    // Api < 17. Unfortunately cannot check for isDestroyed()
                    if (!((Activity) context).isFinishing()) {
                        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

Я добавил в манифест для этого действия

android:configChanges="keyboardHidden|orientation|screenLayout"

Ответ 4

Если у вас есть объект Activity, находящийся вокруг, вы можете использовать метод isDestroyed():

Activity activity;

// ...

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

Это хорошо, если у вас есть не анонимный подкласс AsyncTask, который вы используете в разных местах.

Ответ 5

Я использую собственный статический класс, который отображает и скрывает диалог. этот класс используется другими видами деятельности, а не только одним активом. Теперь проблема, которую вы описали, также мне показалась, и я остался на ночь, чтобы найти решение.

Наконец, я представляю вам решение!

если вы хотите показать или закрыть диалоговое окно, и вы не знаете, какая активность инициировала диалог, чтобы прикоснуться к нему, следующий код для вас.

 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

Для вопроса 1):

Учитывая, что сообщение об ошибке не говорит о том, какая строка вашего кода вызывает проблему, вы можете отслеживать ее с помощью контрольных точек. Точки прерывания приостанавливают выполнение программы, когда программа попадает в определенные строки кода. Добавляя точки останова в критические местоположения, вы можете определить, какая строка кода вызывает сбой. Например, если ваша программа сбой в строке setContentView(), вы можете поставить там точку останова. Когда программа запускается, она приостанавливается до запуска этой строки. Если затем возобновление приводит к сбою программы перед достижением следующей точки останова, вы тогда знаете, что строка, которая убила программу, находилась между двумя точками останова.

Добавление контрольных точек легко, если вы используете Eclipse. Щелкните правой кнопкой мыши по краю слева от вашего кода и выберите "Переключить точку останова". Затем вам нужно запустить приложение в режиме отладки, кнопка, которая выглядит как зеленое насекомое рядом с кнопкой обычного запуска. Когда программа попадает на точку останова, Eclipse переключится на перспективу отладки и покажет вам, в какой строке она находится. Чтобы запустить программу снова, найдите кнопку "Возобновить", которая выглядит как обычная "Play", но с вертикальной полосой слева от треугольника.

Вы также можете заполнить свое приложение Log.d( "Мое приложение", "Некоторая информация здесь, где указано, где находится строка журнала" ), который затем отправляет сообщения в окне Eclipse LogCat. Если вы не можете найти это окно, откройте его с помощью Window → Show View → Other... → Android → LogCat.

Надеюсь, что это поможет!

Ответ 7

Или просто вы можете добавить

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

который сделает ProgressDialog не отмененным

Ответ 8

в соответствии с кодом окнаManager (ссылка здесь), это происходит, когда представление, которое вы пытаетесь обновление (которое, вероятно, принадлежит диалогу, но не обязательно) больше не привязано к реальному корню из окон.

как предложили другие, вы должны проверить статус активности перед выполнением специальных операций в ваших диалогах.

здесь код-relavant, который является причиной проблемы (скопирован из исходного кода 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;
        }
    }

Ответ 9

Моя проблема была решена путем включения вращения экрана на моем андроиде, приложение, которое вызывало у меня проблему, теперь отлично работает

Ответ 10

Другой вариант - не запускать задачу async до тех пор, пока диалоговое окно не будет прикреплено к окну, переопределив onAttachedToWindow() в диалоговом окне, таким образом, это всегда неприемлемо.

Ответ 11

когда вы объявляете активность в манифесте, вам нужен андроид: configChanges = "orientation"

Пример:

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

Ответ 12

Почему бы не попробовать catch, например:

protected void onPostExecute(Object result) {
        try {
            if ((mDialog != null) && mDialog.isShowing()) {
                mDialog.dismiss();
            }
        } catch (Exception ex) {
            Log.e(TAG, ex.getMessage(), ex);
        }
    }