Просмотр не привязан к краху оконного менеджера

Я использую 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

Как воспроизвести ошибку:

  • Включите эту опцию на вашем устройстве: Settings -> Developer Options -> Don't keep Activities.
  • Нажмите кнопку "Домой", пока выполняется AsyncTask, и отобразится ProgressDialog.

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

Как это исправить:

  • Проверьте состояние активности в методе onPostExecute.
  • Отключите метод ProgressDialog в 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);
        }
    }
}

Ответ 2

Проблема может заключаться в том, что Activity были finished или в progress of finishing.

Добавьте проверку isFinishing и отмените диалог, только если это false

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

isFinishing:. Проверьте, выполняется ли эта активность в процессе завершения, либо потому, что вы на нее набрали finish, либо кто-то еще попросил ее закончить.

Ответ 3

Посмотрите, как работает здесь код:

После вызова задачи Async задача async выполняется в фоновом режиме. что желательно. Теперь эта задача Async имеет диалог выполнения, который подключен к Activity, если вы спросите, как это увидеть, см. Код:

pDialog = new ProgressDialog(CLASS.this);

Вы передаете Class.this в качестве контекста для аргумента. Таким образом, диалог "Прогресс" все еще привязан к действию.

Теперь рассмотрим сценарий: Если мы попытаемся завершить работу с использованием метода finish(), в то время как выполняется асинхронная задача, это точка, в которой вы пытаетесь получить доступ к Ресурсу, связанному с активностью, т.е. progess bar, когда активности больше нет.

Следовательно, вы получаете:

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

.

Решение:

1) Убедитесь, что диалоговое окно отклонено или отменено до завершения операции.

2) Завершите действие, только после отклонения диалогового окна, то есть задача async завершена.

Ответ 4

Основываясь на ответе @erakitin, но также совместимом для версий Android < API-интерфейс 17. С грустью Activity.isDestroyed() поддерживается только с уровня API 17, поэтому, если вы настроили таргетинг на более старый уровень 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
        }
    }
}

Ответ 5

Для Dialog, созданного в Fragment, я использую следующий код:

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

Я использую этот шаблон для рассмотрения случая, когда a Fragment можно отделить от Activity.

Ответ 6

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

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

обратитесь к этому.

Ответ 7

Отменить onConfigurationChanged и отменить диалог прогресса. Если диалог прогресса создается в портрете и отменяется в ландшафте, он будет бросать View не привязан к ошибке диспетчера окон.

Также остановите индикатор выполнения и остановите задачу async в onPause(), onBackPressed и onDestroy.

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

    asyncTaskObj.cancel(true);

}

Ответ 8

Во-первых, причина сбоя - индекс 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();
        }

Ответ 9

Переопределить onDestroy из Activity и отклонить ваш диалог и сделать его null

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

Ответ 10

Эта проблема связана с тем, что ваша деятельность завершается до вызова функции увольнения. Обрабатывайте исключение и проверяйте свой журнал 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);
}

Ответ 11

У меня есть способ воспроизвести это исключение. Я использую 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 способа исправить это:

  • @erakitin answer: call isFinishing(), чтобы проверить состояние активности
  • @Kapé answer: установить флаг для проверки состояния активности.
  • Используйте try/catch для его обработки.
  • Вызов AsyncTask.cancel(false) в onDestroy(). Это предотвратит выполнение asynctask onPostExecute(), но вместо этого выполнит onCancelled().
    Примечание: onPostExecute() по-прежнему будет выполняться, даже вы вызываете AsyncTask.cancel(false) на старшую ОС Android, например, Android 2.X.X.

Вы можете выбрать лучший для вас.

Ответ 12

лучшее решение. Проверьте первый контекст - контекст активности или контекст приложения если контекст активности, то только проверка активности завершена или нет, вызовите 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 с помощью условия &&.

Ответ 13

Может быть, вы инициализируете pDialog глобально, затем удалите его и запустите локализовать свое представление или диалог. У меня такая же проблема, я это сделал, и моя проблема решена. Надеюсь, это сработает.

Ответ 14

это очень простой человек, не вызывайте метод, как finish() или не делайте так;

если у вас нет запущенной активности, и вы pDialog.dismiss(); Делая так, как Android узнал, какой диалог о прогрессе активности я должен закрыть?

так что проверяйте свой код и решайте его...

Ответ 15

мы также закрыли наш диалог на методе onPasuse или методе OnDestroyed

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

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