Перегляд не приєднано до аварії менеджера вікон


189

Я використовую ACRA для повідомлення про збої в програмі. Я отримував View not attached to window managerповідомлення про помилку і думав, що виправив його, загорнувши pDialog.dismiss();в оператор if:

if (pDialog!=null) 
{
    if (pDialog.isShowing()) 
    {
        pDialog.dismiss();   
    }
}

Це зменшило кількість View not attached to window managerзбоїв, які я отримую, але я все ще отримую деякі, і я не знаю, як це вирішити.

Повідомлення про помилку:

java.lang.IllegalArgumentException: View not attached to window manager
at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:425)
at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:327)
at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:83)
at android.app.Dialog.dismissDialog(Dialog.java:330)
at android.app.Dialog.dismiss(Dialog.java:312)
at com.package.class$LoadAllProducts.onPostExecute(class.java:624)
at com.package.class$LoadAllProducts.onPostExecute(class.java:1)
at android.os.AsyncTask.finish(AsyncTask.java:631)
at android.os.AsyncTask.access$600(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:176)
at android.app.ActivityThread.main(ActivityThread.java:5419)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
at dalvik.system.NativeStart.main(Native Method)

Фрагмент коду:

class LoadAllProducts extends AsyncTask<String, String, String> 
{

    /**
     * Before starting background thread Show Progress Dialog
     * */
    @Override
    protected void onPreExecute() 
    {
        super.onPreExecute();
        pDialog = new ProgressDialog(CLASS.this);
        pDialog.setMessage("Loading. Please wait...");
        pDialog.setIndeterminate(false);
        pDialog.setCancelable(false);
        pDialog.show();
    }

    /**
     * getting All products from url
     * */
    protected String doInBackground(String... args) 
    {
        // Building Parameters
        doMoreStuff("internet");
        return null;
    }


    /**
     * After completing background task Dismiss the progress dialog
     * **/
    protected void onPostExecute(String file_url) 
    {
         // dismiss the dialog after getting all products
         if (pDialog!=null) 
         {
                if (pDialog.isShowing()) 
                {
                    pDialog.dismiss();   //This is line 624!    
                }
         }
         something(note);
    }
}

Маніфест:

    <activity
        android:name="pagename.CLASS" 
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout"            
        android:label="@string/name" >
    </activity>

Що мені не вистачає, щоб зупинити цю аварію?


1
Ви коли-небудь розбиралися в цьому? У мене така ж проблема. Не можу, зрозуміти.
tomjung

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

Ваш AsyncTaskоголошений всередині Activityабо Fragment?
еракітін

Будь ласка, опублікуйте функції "doMoreStuff ()" та "щось ()".
берсерк

Проблема може виникнути через занадто великої роботи основної нитки, тому спробуйте використовувати обробники, щоб показати progressdialog, і якщо (pDialog! = Null) цей рядок не потрібен, тому що IsShow сам перевірить, чи працює діалог чи ні.
Мадху

Відповіді:


446

Як відтворити помилку:

  1. Увімкніть цю опцію на пристрої: Settings -> Developer Options -> Don't keep Activities.
  2. Натисніть кнопку "Додому", коли AsyncTaskвиконується та ProgressDialogз'являється показ.

ОС Android знищить активність, як тільки вона буде прихована. Коли onPostExecuteназивається Activityзаповіт, він перебуває у стані "закінчення", і ProgressDialogзаповіт не буде приєднаний Activity.

Як це виправити:

  1. Перевірте стан активності у вашому onPostExecuteметоді.
  2. Відхилити метод ProgressDialogin onDestroy. Інакше android.view.WindowLeakedбуде викинуто виняток. Цей виняток зазвичай виникає з діалогів, які все ще активні, коли діяльність закінчується.

Спробуйте цей фіксований код:

public class YourActivity extends Activity {

    private void showProgressDialog() {
        if (pDialog == null) {
            pDialog = new ProgressDialog(StartActivity.this);
            pDialog.setMessage("Loading. Please wait...");
            pDialog.setIndeterminate(false);
            pDialog.setCancelable(false);
        }
        pDialog.show();
    }

    private void dismissProgressDialog() {
        if (pDialog != null && pDialog.isShowing()) {
            pDialog.dismiss();
        }
    }

    @Override
    protected void onDestroy() {
        dismissProgressDialog();
        super.onDestroy();
    }

    class LoadAllProducts extends AsyncTask<String, String, String> {

        // Before starting background thread Show Progress Dialog
        @Override
        protected void onPreExecute() {
            showProgressDialog();
        }

        //getting All products from url
        protected String doInBackground(String... args) {
            doMoreStuff("internet");
            return null;
        }

        // After completing background task Dismiss the progress dialog
        protected void onPostExecute(String file_url) {
            if (YourActivity.this.isDestroyed()) { // or call isFinishing() if min sdk version < 17
                return;
            }
            dismissProgressDialog();
            something(note);
        }
    }
}

9
Чудова відповідь! BTW, isShowing () є непотрібним, оскільки dismiss () нічого не зробить, якщо isShowing () == false. Вихідний код
Пітер Чжао

3
Яка вигода від використання isDestroyed()більш isFinishing()на все API , для цієї конкретної мети?
Олександр Абакумов

@AlexanderAbakumov: Із того, що я розумію, isFinishing()це не гарантовано, trueякщо діяльність буде знищена системою, дивіться документацію про Діяльність .
Маркус Пінгвін

1
@MarkusPenguin: Правильно. Але в цьому випадку, якщо хтось намагався скористатися порадою з коментаря автора // or call isFinishing() if min sdk version < 17, він стикається з тим самим винятком. Отже, нам потрібне інше рішення, ніж ця відповідь для додатків, що працюють на API <17.
Олександр Абакумов,

@AlexanderAbakumov У мене є та сама проблема, але IsFinishing не працює на мене, він працював, зберігаючи слабкі посилання на мою активність всередині AsyncCallback, а потім:myActivityWeakReference.get() != null && !myActivityWeakReference.get().isFinishing()
rusito23

35

Проблема може полягати в тому, що Activityбули finishedабо були progress of finishing.

Додайте чек isFinishingі відмініть діалогове вікно лише тоді, коли це єfalse

if (!YourActivity.this.isFinishing() && pDialog != null) {
    pDialog.dismiss();
}

isFinishing: Перевірте, чи закінчується ця діяльність, або тому, що ви її зателефонували finishчи хтось інший просив її завершити.


1
З чеком виняток все ще кидають
Маркос Васкончелос

Мій ProgressDialog знаходиться в класі, який не є діяльністю, тому я не можу використовувати чек isFinishing @Libin
Maniraj

11

Для Dialogствореного в a Fragmentя використовую такий код:

ProgressDialog myDialog = new ProgressDialog(getActivity());
myDialog.setOwnerActivity(getActivity());
...
Activity activity = myDialog.getOwnerActivity();
if( activity!=null && !activity.isFinishing()) {
    myDialog.dismiss();
}

Я використовую цю схему для вирішення випадку, коли Fragmentможе бути від'єднаний від Activity.


8

Дивіться, як працює Кодекс тут:

Після виклику завдання Async завдання async працює у фоновому режимі. що бажано. Тепер це завдання Async має діалог прогресу, який додається до Діяльності, якщо ви запитаєте, як побачити код:

pDialog = new ProgressDialog(CLASS.this);

Ви передаєте Class.thisаргумент як контекст. Тож діалог "Прогрес" все ще додається до діяльності.

Тепер розглянемо сценарій: Якщо ми намагаємося закінчити діяльність за допомогою методу закінчення (), поки завдання асинхронізації виконується, це пункт, коли ви намагаєтеся отримати доступ до ресурсу, приєднаного до діяльності, тобто progress barколи активність більше не є там.

Отже, ви отримуєте:

java.lang.IllegalArgumentException: View not attached to the window manager

Рішення цього:

1) Переконайтесь, що діалогове вікно відхилено або скасовано до того, як діяльність закінчиться.

2) Закінчіть діяльність лише після відхилення діалогового вікна, тобто завдання асинхронізації закінчено.


1
Щодо № 2: ви не маєте повного контролю над тим, коли закінчити Activity; Android може закінчити його будь-коли. Отже, №2 не має сенсу.
Олександр Абакумов

7

На основі відповіді @erakitin, але також сумісний для версій Android <API рівень 17. На жаль Activity.isDestroyed () підтримується лише з рівня 17 API, тому якщо ви орієнтуєтесь на більш старий рівень API, як я, вам доведеться перевірте самі. View not attached to window managerВиняток після цього не отримав .

Приклад коду

public class MainActivity extends Activity {
    private TestAsyncTask mAsyncTask;
    private ProgressDialog mProgressDialog;
    private boolean mIsDestroyed;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (condition) {
            mAsyncTask = new TestAsyncTask();
            mAsyncTask.execute();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        if (mAsyncTask != null && mAsyncTask.getStatus() != AsyncTask.Status.FINISHED) {
            Toast.makeText(this, "Still loading", Toast.LENGTH_LONG).show();
            return;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mIsDestroyed = true;

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

    public class TestAsyncTask extends AsyncTask<Void, Void, AsyncResult> {    
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mProgressDialog = ProgressDialog.show(MainActivity.this, "Please wait", "doing stuff..");
        }

        @Override
        protected AsyncResult doInBackground(Void... arg0) {
            // Do long running background stuff
            return null;
        }

        @Override
        protected void onPostExecute(AsyncResult result) {
            // Use MainActivity.this.isDestroyed() when targeting API level 17 or higher
            if (mIsDestroyed)// Activity not there anymore
                return;

            mProgressDialog.dismiss();
            // Handle rest onPostExecute
        }
    }
}

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

    if(pDialog != null)
        pDialog .dismiss();
    pDialog = null;
}

зверніться до цього .


3

Замініть параметр onConfigurationChanged та відключіть діалог прогресу. Якщо діалогове вікно прогрес буде створено в портретному режимі і відхиляється в пейзажному режимі, то він видасть «Вигляд не приєднаний» до помилки менеджера вікон.

Також зупиніть панель ходу та зупиніть завдання асинхронізації в методі onPause (), onBackPress та onDestroy.

if(asyncTaskObj !=null && asyncTaskObj.getStatus().equals(AsyncTask.Status.RUNNING)){

    asyncTaskObj.cancel(true);

}

3

Замініть функцію onDestroy діяльності та відпустіть діалог та зробіть його недійсним

protected void onDestroy ()
    {
        if(mProgressDialog != null)
            if(mProgressDialog.isShowing())
                mProgressDialog.dismiss();
        mProgressDialog= null;
    }

3

По-перше, причина збоїв в тому, що індекс decorView дорівнює -1, ми можемо це знати з вихідного коду Android, є фрагмент коду:

клас: android.view.WindowManagerGlobal

файл: WindowManagerGlobal.java

private int findViewLocked(View view, boolean required) {
        final int index = mViews.indexOf(view);
//here, view is decorView,comment by OF
        if (required && index < 0) {
            throw new IllegalArgumentException("View=" + view + " not attached to window manager");
        }
        return index;
    }

Таким чином, ми отримуємо наступну роздільну здатність, просто судіть індекс decorView, якщо він більше 0, тоді продовжуйте або просто повертайтеся та відмовляйтеся від відхилення, кодуйте так:

try {
            Class<?> windowMgrGloable = Class.forName("android.view.WindowManagerGlobal");
            try {
                Method mtdGetIntance = windowMgrGloable.getDeclaredMethod("getInstance");
                mtdGetIntance.setAccessible(true);
                try {
                    Object windownGlobal = mtdGetIntance.invoke(null,null);
                    try {
                        Field mViewField = windowMgrGloable.getDeclaredField("mViews");
                        mViewField.setAccessible(true);
                        ArrayList<View> mViews = (ArrayList<View>) mViewField.get(windownGlobal);
                        int decorViewIndex = mViews.indexOf(pd.getWindow().getDecorView());
                        Log.i(TAG,"check index:"+decorViewIndex);
                        if (decorViewIndex < 0) {
                            return;
                        }
                    } catch (NoSuchFieldException e) {
                        e.printStackTrace();
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        if (pd.isShowing()) {
            pd.dismiss();
        }

2

Навколо такого dismiss()методу:

@Override
public void dismiss() {
    Window window = getWindow();
    if (window == null) {
        return;
    }
    View decor = window.getDecorView();
    if (decor != null && decor.getParent() != null) {
        super.dismiss();
    }
}

Щоб відтворити проблему, просто закінчіть діяльність перед діалогом відхилення.


1

найкраще рішення. Перевірити перший контекст - це контекст діяльності або контекст програми, якщо контекст діяльності, то лише активність перевірки завершена, або не потім викликати dialog.show()абоdialog.dismiss();

Дивіться зразок коду нижче ... сподіваюся, що він буде корисним!

Діалогове вікно відображення

if (context instanceof Activity) {
   if (!((Activity) context).isFinishing())
     dialog.show();
}

Діалогове вікно відхилення

if (context instanceof Activity) {
       if (!((Activity) context).isFinishing())
         dialog.dismiss();
    }

Якщо ви хочете додати більше чеків, додайте dialog.isShowing()або dialog !-nullвикористовуйте &&умову.


0

Ця проблема пов'язана з тим, що ваша діяльність закінчується до виклику функції звільнення. Обробіть виняток і перевірте свій журнал ADB на точну причину.

/**
     * After completing background task Dismiss the progress dialog
     * **/
    protected void onPostExecute(String file_url) {
    try {
         if (pDialog!=null) {
            pDialog.dismiss();   //This is line 624!    
         }
    } catch (Exception e) {
        // do nothing
    }
     something(note);
}

0

Я отримав спосіб відтворити цей виняток.

Я використовую 2 AsyncTask. Один виконує довге завдання, а інший виконує коротке завдання. Після завершення короткого завдання зателефонуйте finish(). Коли довге завдання завершено і зателефонує Dialog.dismiss(), воно вийде з ладу.

Ось мій зразок коду:

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";
    private ProgressDialog mProgressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate");

        new AsyncTask<Void, Void, Void>(){
            @Override
            protected void onPreExecute() {
                mProgressDialog = ProgressDialog.show(MainActivity.this, "", "plz wait...", true);
            }

            @Override
            protected Void doInBackground(Void... nothing) {
                try {
                    Log.d(TAG, "long thread doInBackground");
                    Thread.sleep(20000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                return null;
            }

            @Override
            protected void onPostExecute(Void result) {
                Log.d(TAG, "long thread onPostExecute");
                if (mProgressDialog != null && mProgressDialog.isShowing()) {
                    mProgressDialog.dismiss();
                    mProgressDialog = null;
                }
                Log.d(TAG, "long thread onPostExecute call dismiss");
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

        new AsyncTask<Void, Void, Void>(){
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    Log.d(TAG, "short thread doInBackground");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            protected void onPostExecute(Void aVoid) {
                super.onPostExecute(aVoid);
                Log.d(TAG, "short thread onPostExecute");
                finish();
                Log.d(TAG, "short thread onPostExecute call finish");
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }
}

Ви можете спробувати це і з’ясувати, який найкращий спосіб виправити цю проблему. З мого дослідження, є щонайменше 4 способи виправити це:

  1. @ erakitin відповідь: зателефонуйте, isFinishing()щоб перевірити стан діяльності
  2. @ Відповідь Капе: встановіть прапор, щоб перевірити стан активності
  3. Використовуйте спробувати / catch для впорядкування.
  4. Телефонуйте AsyncTask.cancel(false)в onDestroy(). Це не дозволить асинтактові виконувати, onPostExecute()а виконувати onCancelled()замість цього.
    Примітка. onPostExecute()Ви все одно будете виконувати навіть дзвінки AsyncTask.cancel(false)на старих ОС Android, таких як Android 2.XX

Ви можете вибрати найкращий для вас.


0

Можливо, ви ініціалізуєте pDialog у всьому світі, потім видаліть його та активізуйте свій погляд або діалог локально. У мене така ж проблема, я це зробив, і моя проблема вирішена. Сподіваюся, це буде працювати для u.


0

ми також відхилили наш діалог про onPauseспосіб чи onDestroyметод

@Override
protected void onPause() {
    super.onPause();
    dialog.dismiss();
}

@Override
protected void onDestroy() {
    super.onDestroy();
    dialog.dismiss();
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.