Android View не привязан к оконному менеджеру - PullRequest
109 голосов
/ 08 февраля 2010

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

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 ]

160 голосов
/ 05 июня 2011

У меня была эта проблема, когда при изменении ориентации экрана действие завершалось до 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();
   }
}
8 голосов
/ 17 января 2017

После борьбы с этой проблемой я наконец-то нашел этот обходной путь:

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

Иногда хорошая обработка исключений работает хорошо, если не было лучшего решения для этой проблемы.

5 голосов
/ 13 марта 2015

Если у вас висит объект Activity, вы можете использовать метод isDestroyed():

Activity activity;

// ...

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

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

3 голосов
/ 12 сентября 2015

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

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

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

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


            }
        } 

 }
1 голос
/ 30 января 2018

Вышеупомянутое решение не работает для меня. Итак, я взял ProgressDialog как глобально, а затем добавил это к моей деятельности

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

так что в случае, если активность уничтожена, ProgressDialog также будет уничтожен.

1 голос
/ 25 апреля 2011

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

android:configChanges="keyboardHidden|orientation|screenLayout"
0 голосов
/ 18 октября 2016

Почему бы не попробовать поймать, как это:

protected void onPostExecute(Object result) {
        try {
            if ((mDialog != null) && mDialog.isShowing()) {
                mDialog.dismiss();
            }
        } catch (Exception ex) {
            Log.e(TAG, ex.getMessage(), ex);
        }
    }
0 голосов
/ 06 мая 2014

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

0 голосов
/ 18 апреля 2014

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

0 голосов
/ 23 декабря 2013

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

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

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