java.lang.IllegalArgumentException: Перегляд не додано до менеджера вікон


148

У мене є діяльність, яка запускає 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, схоже на єдину можливу ситуацію, щоб отримати це виняток - коли діяльність була знищена. Але, як я вже згадував, я забороняю активний відпочинок для основних подій.

Тож будь-які пропозиції дуже вдячні.


1
На це запитання отримано багато відповідей, якщо будь-який із них допоміг вам обрати його як правильну відповідь.
Параг Кадам

Відповіді:


228

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

44
Просте рішення? Так. Ефективний? Можливо, в цьому випадку. Я б рекомендував його? НЕМАЄ! Не ковтайте ВСІ винятки, як це! Я б навіть не спіймав IllegalArgumentException, але шукав інше рішення.
Саймон Форсберг

6
Тому що зазвичай порожні спроби лову - це погана ідея ... Хоча іноді це можна зробити правильно.
Томас

3
@Damjan Вашою відповіддю ви пропонуєте тип вилову Виняток. Ну, це погана практика від Google. Про це можна прочитати тут: Не ловити загальне виключення .
Янів

17
Я вважаю, що це ефективне виправлення. У загальних випадках ми не повинні цього робити, але оскільки Android Framework не забезпечує для нас простої перевірки, ми повинні використовувати незвичний спосіб. Крім того, якщо дзвінок isShowing () діалогу працює так, як ми очікували, нам не потрібен такий вид зламу.
SXC

1
швидко виправити, поки не знайдеться щось краще
Rohit Tigga

13

Ось моє "куленепробивне" рішення - це збірка всіх хороших відповідей, які я знайшов у цій темі (дякую @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;
        }
    }

1
дуже хороше рішення! Налаштування dialog = nullне впливає. І StatusEventDialogслід читати просто Dialog.
hgoebl

1
StatusEventDialog слід змінити на діалогове.
Sreekanth Karumanaghat

Ця відповідь має бути прийнятою, дуже добре
впорядкованою

Я розумію, що ви хочете бути "правильним", і використовуйте лише try / catch лише в тому випадку, якщо isDestroyed()він недоступний, але для практичних цілей не було б таким самим завжди використовувати спробувати / catch?
зунді

11

У мене може бути вирішення проблеми.

У мене була така ж проблема, коли я завантажую багато елементів (за допомогою файлової системи) у програму ListViewvia AsyncTask. Було onPreExecute()запущено a ProgressDialog, а потім обидва onPostExecute()та onCancelled()(викликається, коли завдання скасовано явно через AsyncTask.cancel()), закривши його через .cancel().

Отримав таку ж помилку "java.lang.IllegalArgumentException: Перегляд не приєднаний до менеджера вікон", коли я вбивав діалогове вікно в onCancelled()методі AsyncTask(я бачив це в відмінному додатку "Полиці" ).

Вирішення проблеми полягало у створенні загальнодоступного поля у тому, AsyncTaskщо містить ProgressDialog:

public ProgressDialog mDialog;

Потім, onDestroy()коли я скасовую свою AsyncTask, я також можу вбити пов'язаний діалог через:

AsyncTask.mDialog.cancel();

Виклик AsyncTask.cancel()тригера DOES onCancelled()у AsyncTask, але з певних причин до того часу, коли цей метод викликається, Перегляд уже знищено, і, таким чином, скасовуючи діалогове вікно не вдалося.


Я вважаю реалізацію UserTask просто чудовою, як згадував @Paul. Вихідний код тут: code.google.com/p/shelves/source/browse/trunk/Shelves/src/org/…
Пісня Evi

Хоча випадок використання можна знайти в тому самому проекті: code.google.com/p/shelves/source/browse/trunk/Shelves/src/org/…
Evi Song

9

Ось правильне рішення для вирішення цієї проблеми:

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.


3
isDestroyedпотрібно API 17+
Androiderson

Чому потрібно встановити mProgressDialog на нульове значення? Це пов’язано з витоком пам’яті? Не могли б ви пояснити?
Pawan

@Pawan, це детальна інформація про моє завершення. Це не потрібно, це просто так, як функціонує цей клас. Після того, як діалог прогресу буде приховано, я встановлюю його на нульове значення. Коли користувач хоче показати інший діалог прогресу, новий екземпляр створюється миттєво.
Качі

Однозначно! ((Діяльність) контекст) .isFinishing () потрібно, дякую! :)
Даніель Кшицковський

5

Я згоден з думкою "Дам'яна".
якщо ви використовуєте багато діалогів, слід закрити все діалогове вікно в onDestroy () або onStop ().
то, можливо, ви зможете зменшити частоту "java.lang.IllegalArgumentException: Вигляд не приєднаний до менеджера вікон".

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


Щасти!


Я завжди вважаю за краще уникати порожніх блоків лову. варто спробувати, але оскільки цю помилку важко створити - лише час покаже, чи справді вона працює. все одно, дякую.
Dror Fichman

що таке порожні блоки вилову? я не використовую спробу / ловити. змінна mIsDestroyed перевищена. але якщо ви пишете в код, що з'явиться у діалоговому вікні після роботи в іншій потоці, вам може знадобитися ця змінна. коли працює інший потік, якщо діяльність закінчена, ви можете переглянути цей виняток.
Хогун

У мене була така ж проблема, і коли я додав @Override public void onPause () {if (dialog! = Null) dialog.dismiss (); super.onPause (); } до цих пір я не маю цієї помилки ... тому я вважаю, що це так само, як і ваша відповідь, і вона справді корисна
Кріс Сім,

@ChrisSim привіт! onPuase () і onDestroy () - це різниця. коли активність увімкнено, діалог закритий. а після запуску програми діалогове вікно не відображається. ти цього хочеш?
Хогун

@Hogun Так, звичайно, я маю на увазі ту саму ідею, я закриваю діалог про паузу, а не на знищення, тому що мені це потрібно в паузі, а не в знищенні. По-друге, я закриваю його просто тоді, коли це не є нульовим. Дякую, що пояснили це іншим.
Кріс Сім

4

Використовуй це.

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

2
Це майже те саме рішення, яке запропонував @Damjan.
Юрій

28
Цього недостатньо, IllegalArgumentException все ще відбувається з цією перевіркою.
Мерфі

Я зробив те саме рішення, але досі не знаю, чи ефективний він. Тільки різниця, я вклав два, якщо впевнений, що друга частина .isShowing () не буде оцінена, якщо вона недійсна.
Нік

2
Цього недостатньо.
trante

1
@ Nick: Немає необхідності вкладати декілька ifв таких випадках, &&оператор Java має ледачу оцінку (також її називають короткою ланцюговою схемою ), що означає, що 2-й операнд не оцінюється, якщо перший оцінює false(що означає результат &&завжди буде в falseбудь-якому випадку, отже , «ліниві» оцінка). Аналогічно ||не буде оцінювати свій 2-й операнд, якщо перший оцінить до true. Примітка: оператори &та |оператори не мають такої поведінки, і тому завжди оцінюють обидва операнди.
Маттіас

3

У мене була така ж проблема, ви можете її вирішити:

@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

Я думаю, що ваш код правильний на відміну від запропонованої іншої відповіді. onPostExecute буде працювати на потоці інтерфейсу користувача. В цьому і полягає суть AsyncTask - вам не потрібно турбуватися про виклик runOnUiThread або справу з обробниками. Крім того, згідно з документами, dismiss () можна сміливо викликати з будь-якої нитки (не впевнений, що вони зробили це виключенням).

Можливо, це питання про терміни, коли виклик dialog.dismiss () після того, як активність більше не відображається?

Що з тестуванням того, що станеться, якщо ви прокоментуєте setOnCancelListener, а потім вийдете з дії під час виконання фонового завдання? Тоді ваш onPostExecute спробує відхилити вже відхилений діалог. Якщо програма виходить з ладу, ви, ймовірно, просто перевірте, чи відкрито діалогове вікно, перш ніж відхиляти його.

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


Я також заглянув у код dimiss (), і справді його можна сміливо викликати з будь-якої нитки. До речі, у мене виникають проблеми з тестуванням, оскільки ця проблема трапляється на телефонах користувачів, і я ніколи не міг відтворити сам :-( Так намагався розібратися, аналізуючи код ... Відповідно до часу. Я думав над цим, але не можу уявити ситуацію, як активність можна закрити перед діалоговим вікном. Якщо натиснути кнопку BACK, вона спершу скасує діалогове вікно. А автоматичне відтворення активності заборонено файлом маніфесту, але, можливо, його все одно можна відтворити, як? я знаю, якщо ти знайдеш якусь річ!
alex2k8

2

Алекс,

Я можу помилитися тут, але я підозрюю, що кілька телефонів "у дикій природі" мають помилку, через яку вони перемикають орієнтацію на додатки, позначені як статично орієнтовані. Це відбувається зовсім небагато на моєму особистому телефоні, і на багатьох тестових телефонах, які використовує наша група (включаючи droid, n1, g1, hero). Зазвичай додаток, позначений як статично орієнтований (можливо, вертикально), викладеться на секунду-дві за допомогою горизонтальної орієнтації, а потім негайно переключиться назад. Кінцевий результат полягає в тому, що, хоча ви не хочете, щоб ваш додаток перемикало орієнтацію, ви повинні бути готові, що це може. Я не знаю, при яких конкретних умовах може бути відтворена така поведінка, я не знаю, чи стосується вона версії Android. Все, що я знаю, це те, що я бачив це багато разів :(

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


1
Ви можете утримувати свій пристрій від перемикання орієнтацій, але є маса інших змін конфігурацій, які руйнують / відтворюють вашу активність - звичайна - це просування клавіатури увімкнення або вимкнення.
MaximumGoat

2

Що найбільше працювало для мене, це перевірити, чи не закінчується Діяльність.

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

2

Діяльність оголошена НЕ відтворена за допомогою обертання або слайду клавіатури.

Просто отримала таку ж проблему. Виправити для API рівня 13 або вище.
З Документів Android:

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

Тому я змінив свій маніфест на це:

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

А зараз це чудово працює. Діяльність не відтворюється, коли я повертаю телефон, діалогове вікно перегляду та перегляд залишаються незмінними. Для мене жодної помилки.


це не рішення. наприклад, у мене є активність і Adview від admob, і вона повинна відтворити активність для зміни розміру.
batmaci

2

Перш за все зробіть помилку, де ви намагаєтесь відхилити діалогове вікно.

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

1

У мене була така ж проблема під час використання кнопки для синхронізації списку з сервера: 1) натискаю кнопку 2) з'являється діалогове вікно прогресу під час завантаження списку з сервера 3) я перемикаю пристрій на іншу орієнтацію 4) java.lang .IllegalArgumentException: Перегляд не додається до менеджера вікон на postExecute () AsyncTask під час progress.dismiss ().

Коли я намагався виправити, я зрозумів, що навіть якщо проблема не виникає, у моєму списку не відображаються всі елементи.

Я зрозумів, що я хотів, щоб AsyncTask закінчив (і відхилив діалогове вікно) перед тим, як активність буде знищена, тому я зробив об'єкт asynctask атрибутом і змінив метод onDestroy ().

Якщо асинтакт забирає багато часу, користувач, можливо, відчує, що пристрій повільний, але я думаю, що це ціна, яку він платить за спробу змінити орієнтацію пристрою, поки з'явиться діалог прогрес. І навіть якщо це займе деякий час, додаток не вийде з ладу.

private AsyncTask<Boolean, Void, Boolean> atask;

@Override
protected void onDestroy() {
    if (atask!=null)
        try {
            atask.get();
        } catch (InterruptedException e) {
        } catch (ExecutionException e) {
        }
    super.onDestroy();
}

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

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

3
Хоча цей фрагмент коду може відповісти на запитання, надавши пояснення, як він вирішує проблему, допоможе майбутнім відвідувачам сайту зрозуміти вашу відповідь
RobV

0

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

Не визначайте свій погляд у всьому світі, якщо ви називаєте його з фонової служби.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.