Java.lang.IllegalArgumentException: просмотр не привязан к менеджеру окон

У меня есть активность, которая запускает AsyncTask и показывает диалог выполнения в течение продолжительности операции. Активность объявлена ​​НЕ воссозданной вращением или слайдом клавиатуры.

    <activity android:name=".MyActivity" 
              android:label="@string/app_name"
              android:configChanges="keyboardHidden|orientation"
              >
        <intent-filter>
        </intent-filter>
    </activity>

Как только задача завершена, я отлаживаю диалог, но на некоторых телефонах (framework: 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, похоже, единственная возможная ситуация, чтобы получить это исключение когда деятельность была разрушена. Но, как я уже упоминал, я запрещаю активный отдых для основных событий.

Поэтому любые предложения очень ценятся.

Ответ 1

Я также получаю эту ошибку иногда, когда я увольняю диалог и завершаю работу по методу 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;
    }  
}

Ответ 2

У меня может быть обходное решение.

Имел ту же проблему, когда я загружаю много элементов (через файловую систему) в ListView через AsyncTask. Если onPreExecute() запускает a ProgressDialog, а затем и onPostExecute() и onCancelled() (вызывается, когда задача явно отменяется с помощью AsyncTask.cancel()), закрывая ее с помощью .cancel().

Получил ту же ошибку "java.lang.IllegalArgumentException: View not attach to window manager", когда я убил диалог в методе onCancelled() AsyncTask (я видел это в превосходном Полки приложение).

Обходной путь состоял в том, чтобы создать публичное поле в AsyncTask, которое содержит ProgressDialog:

public ProgressDialog mDialog;

Затем, в onDestroy(), когда я отменяю свой AsyncTask, я также могу убить связанный диалог с помощью:

AsyncTask.mDialog.cancel();

Вызов AsyncTask.cancel() Устанавливает onCancelled() в AsyncTask, но по какой-то причине к моменту вызова этого метода представление уже уничтожено и, таким образом, отмена диалога не выполняется.

Ответ 3

Вот правильное решение этой проблемы:

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

Вместо того, чтобы слепо перехватывать все исключения, это решение устраняет корень проблемы: попытка уменьшить диалог, когда активность, используемая для инициализации диалога, уже завершена. Работа над моим Nexus 4 работает KitKat, но должна работать для всех версий Android.

Ответ 4

Вот мое "пуленепробиваемое" решение, которое является компиляцией всех хороших ответов, которые я нашел на эту тему (спасибо @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

Я согласен с мнением "Дамьяна".
если вы используете много диалогов, следует закрыть все диалоговые окна в onDestroy() или onStop().
то вы можете уменьшить частоту возникновения исключения java.lang.IllegalArgumentException: View, не связанного с диспетчером окон.

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


Удачи!

Ответ 6

Используйте это.

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

Ответ 7

У меня была та же проблема, вы можете решить ее:

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

Ответ 8

Я думаю, что ваш код правильный, в отличие от другого предложенного ответа. onPostExecute будет работать в потоке пользовательского интерфейса. То, что весь смысл AsyncTask - вам не нужно беспокоиться о вызове runOnUiThread или иметь дело с обработчиками. Кроме того, согласно документам, remove() можно безопасно вызвать из любого потока (не уверен, что они сделали это исключением).

Возможно, это проблема времени, когда dialog.dismiss() вызывается после того, как активность больше не отображается?

Как насчет тестирования того, что произойдет, если вы закомментируете setOnCancelListener и затем выйдите из действия во время выполнения фоновой задачи? Затем ваш onPostExecute попытается отклонить уже отложенный диалог. Если приложение выйдет из строя, вы можете просто проверить, открыто ли диалоговое окно, прежде чем отклонить его.

У меня такая же проблема, поэтому я собираюсь попробовать ее в коде.

Ответ 9

alex,

Я могу ошибаться здесь, но я подозреваю, что у нескольких телефонов "в дикой природе" есть ошибка, из-за которой они переключают ориентацию на приложения, отмеченные как статически ориентированные. Это происходит совсем немного на моем личном телефоне и на многих тестовых телефонах, которые использует наша группа (в том числе дроид, n1, g1, герой). Обычно приложение, помеченное как статически ориентированное (возможно, вертикально), будет выходить на второй или второй с горизонтальной ориентацией, а затем сразу же переключиться обратно. Конечным результатом является то, что даже если вы не хотите, чтобы ваше приложение переключало ориентацию, вы должны быть готовы к тому, что это возможно. Я не знаю, в каких точных условиях это поведение может быть воспроизведено, я не знаю, относится ли это к версии Android. Все, что я знаю, это то, что я видел это много раз: (

Я бы рекомендовал использовать предоставленное решение в опубликованной вами ссылке, в котором предлагается переопределить метод Activity onCreateDialog и позволить ОС Android управлять жизненным циклом ваших диалогов. Мне кажется, что даже если вы не хотите, чтобы ваша деятельность переключала ориентацию, она куда-то переключается. Вы можете попытаться отследить метод, который всегда будет препятствовать переключению ориентации, но я пытаюсь сказать вам, что лично я не считаю, что существует надежный способ, который работает на всех современных телефонах Android на рынке.

Ответ 10

То, что работало для меня большую часть времени, - проверить, не завершено ли действие.

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

Ответ 11

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

 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) Я нажимаю кнопку 2) Диалог прогресса отображается при загрузке списка с сервера 3) Переключаю устройство на другую ориентацию 4) java.lang.IllegalArgumentException: просмотр не привязан к оконному менеджеру в postExecute() AsyncTask во время progress.dismiss().

Как я испробовал исправить, я понял, что даже если проблема не возникает, мой список не показывал все элементы.

Я понял, что я хотел, чтобы 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

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

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

Ответ 14

Активность объявляется НЕ воссозданной вращением или слайдом клавиатуры.

Просто такая же проблема. Исправьте уровень API 13 или более высокий.
От Android-документов:

Примечание. Если ваше приложение нацелено на уровень API 13 или выше (как указано атрибутами minSdkVersion и targetSdkVersion), вы также должны объявить конфигурацию "screenSize", так как она также изменяется, когда устройство переключается между портретной и альбомной ориентацией.

Итак, я изменил свой манифест на это:

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

И теперь это прекрасно работает. Активность не воссоздается, когда я поворачиваю телефон, диалог выполнения и вид остаются неизменными. Нет ошибки для меня.

Ответ 15

Для вас работает Migh ниже кода, он отлично работает для меня:

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

Не определяйте свой взгляд глобально, если и вызываете его из фоновой службы.