La vista no está asociada al bloqueo del administrador de ventanas

190

Estoy usando ACRA para reportar bloqueos de aplicaciones. Estaba recibiendo un View not attached to window managermensaje de error y pensé que lo había solucionado envolviendo el pDialog.dismiss();en una declaración if:

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

Se ha reducido la cantidad de View not attached to window manageraccidentes que recibo, pero todavía estoy recibiendo algunos y no estoy seguro de cómo solucionarlo.

Mensaje de error:

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)

Fragmento de código:

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

Manifiesto:

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

¿Qué me falta para evitar que ocurra este bloqueo?

Howli
fuente
1
¿Alguna vez te diste cuenta de esto? Estoy teniendo el mismo problema. Parece que no puedo entenderlo.
tomjung
Lamentablemente no. Podría comenzar una recompensa en un momento. Debe consultar algunos de los otros hilos que se ocupan de este problema en caso de que lo ayuden.
Howli
Tu AsyncTaskse declara dentro Activityo Fragment?
erakitin
Publique las funciones "doMoreStuff ()" y "something ()".
loco
El problema puede deberse a demasiado trabajo del hilo principal, así que intente usar controladores para mostrar el cuadro de diálogo de progreso, y si (pDialog! = Null) esta línea no es necesaria, porque está mostrándose si el diálogo está en progreso o no.
Madhu

Respuestas:

446

Cómo reproducir el error:

  1. Activar esta opción en su dispositivo: Settings -> Developer Options -> Don't keep Activities.
  2. Presione el botón Inicio mientras AsyncTaskse ejecuta y ProgressDialogse muestra.

El sistema operativo Android destruirá una actividad tan pronto como esté oculta. Cuando onPostExecutese llama Activity, estará en estado de "finalización" y ProgressDialogno se adjuntará Activity.

Como arreglarlo:

  1. Verifique el estado de la actividad en su onPostExecutemétodo.
  2. Descarte el método ProgressDialogin onDestroy. De lo contrario, se android.view.WindowLeakedlanzará una excepción. Esta excepción generalmente proviene de diálogos que aún están activos cuando la actividad está terminando.

Prueba este código fijo:

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);
        }
    }
}
erakitin
fuente
10
¡Gran respuesta! Por cierto, isShowing () es innecesario porque despedir () no hará nada si isShowing () == falso. Código fuente
Peter Zhao
3
¿Cuál es el beneficio de usar isDestroyed()más isFinishing()en todas las API para este propósito específico?
Alexander Abakumov
@AlexanderAbakumov: Por lo que entiendo isFinishing()no se garantiza que sea truesi la actividad es destruida por el sistema, consulte la documentación sobre Actividades .
Markus Penguin
1
@MarkusPenguin: Correcto. Pero, en este caso, si uno intenta usar un consejo del comentario del autor // or call isFinishing() if min sdk version < 17, se encuentra con la misma excepción. Por lo tanto, necesitamos una solución diferente a esta respuesta para aplicaciones que se ejecutan en API <17.
Alexander Abakumov
@AlexanderAbakumov Estoy teniendo el mismo problema, pero el acabado no funciona para mí, funcionó guardando una débil referencia a mi actividad dentro del AsyncCallback, y luego:myActivityWeakReference.get() != null && !myActivityWeakReference.get().isFinishing()
rusito23
36

El problema podría ser que Activityhan estado finishedo enprogress of finishing .

Agregue un cheque isFinishingy descarte el diálogo solo cuando esto seafalse

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

isFinishing: verifique si esta actividad está en proceso de finalización, ya sea porque lo solicitó finisho si alguien más ha solicitado que finalice.

Libin
fuente
1
Con el cheque la excepción todavía se está lanzando
Marcos Vasconcelos
Mi ProgressDialog está en una clase que no es una actividad, así que no puedo usar el cheque isFinishing @Libin
Maniraj el
12

Para Dialogcreado en a Fragment, uso el siguiente código:

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

Utilizo este patrón para tratar el caso cuando un Fragmentpuede ser separado del Activity.

Teng-pao Yu
fuente
8

Vea cómo funciona el Código aquí:

Después de llamar a la tarea asincrónica, la tarea asincrónica se ejecuta en segundo plano. Eso es deseable. Ahora, esta tarea Async tiene un cuadro de diálogo de progreso que se adjunta a la Actividad, si pregunta cómo ver el código:

pDialog = new ProgressDialog(CLASS.this);

Estás pasando el Class.thiscontexto como al argumento. Por lo tanto, el cuadro de diálogo Progreso todavía está adjunto a la actividad.

Ahora considere el escenario: si intentamos finalizar la actividad utilizando el método finish (), mientras la tarea asíncrona está en progreso, es el punto donde está intentando acceder al Recurso adjunto a la actividad, es decir, el progress bar cuando la actividad ya no está ahí.

Por lo tanto, obtienes:

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

Solución a esto:

1) Asegúrese de que el cuadro de diálogo se descarta o cancela antes de que finalice la actividad.

2) Finalice la actividad, solo después de cerrar el cuadro de diálogo, es decir, la tarea asincrónica ha terminado.

Kailas
fuente
1
Con respecto al n. ° 2: no tienes control total sobre cuándo terminar el Activity; Android puede terminarlo en cualquier momento. Entonces, el # 2 no tiene sentido.
Alexander Abakumov
7

Basado en la respuesta @erakitin, pero también es compatible con las versiones de Android <nivel API 17. Lamentablemente, Activity.isDestroyed () solo es compatible desde el nivel API 17, por lo que si está apuntando a un nivel API más antiguo como yo, tendrá que compruébalo tú mismo. No tengo la View not attached to window managerexcepción después de eso.

Código de ejemplo

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
        }
    }
}
Kapé
fuente
4
@Override
public void onPause() {
    super.onPause();

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

referir esto .

Meghna
fuente
3

Anular onConfigurationChanged y descartar el diálogo de progreso. Si el diálogo de progreso se crea en vertical y se descarta en horizontal, arrojará el error Vista no adjunta al administrador de ventanas.

También detenga la barra de progreso y detenga la tarea asíncrona en los métodos onPause (), onBackPressed y onDestroy.

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

    asyncTaskObj.cancel(true);

}
Pratapi Hemant Patel
fuente
3

Anular onDestruir la actividad y descartar su diálogo y hacerlo nulo

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

En primer lugar, el motivo del bloqueo es que el índice de decorView es -1, podemos saberlo por el código fuente de Android, hay un fragmento de código:

clase: android.view.WindowManagerGlobal

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

así que obtenemos la resolución de seguimiento, solo juzgue el índice de decorView, si es más de 0, continúe o simplemente regrese y abandone el descarte, codifique de la siguiente manera:

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

Anule el dismiss()método como este:

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

Para reproducir el problema, simplemente finalice la actividad antes de cerrar el cuadro de diálogo.

Thundertrick
fuente
1

mejor solución. Verificar el primer contexto es el contexto de la actividad o el contexto de la aplicación si el contexto de la actividad solo verifica que la actividad haya finalizado o no, luego llame dialog.show()odialog.dismiss();

Vea el código de muestra a continuación ... ¡espero que sea útil!

Mostrar diálogo

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

Descartar diálogo

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

Si desea agregar más comprobaciones, agregue dialog.isShowing()o dialog !-nulluse la &&condición.

Yogesh Rathi
fuente
0

Este problema se debe a que su actividad finaliza antes de que se llame a la función de descarte. Maneje la excepción y verifique su registro ADB por la razón exacta.

/**
     * 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);
}
Rajiv Manivannan
fuente
0

Tengo una manera de reproducir esta excepción.

Yo uso 2 AsyncTask. Uno hace una tarea larga y otro hace una tarea corta. Después de completar la tarea corta, llame finish(). Cuando se completa una tarea larga y se llama Dialog.dismiss(), se bloquea.

Aquí está mi código de muestra:

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

Puede probar esto y descubrir cuál es la mejor manera de solucionar este problema. De mi estudio, hay al menos 4 formas de solucionarlo:

  1. Respuesta de @ erakitin: llame isFinishing()para verificar el estado de la actividad
  2. Respuesta de @ Kapé: establezca la bandera para verificar el estado de la actividad
  3. Use try / catch para manejarlo.
  4. Llamar AsyncTask.cancel(false)en onDestroy(). Evitará que el asinctask se ejecute, onPostExecute()pero se ejecutará en su onCancelled()lugar.
    Nota: onPostExecute()aún se ejecutará incluso si llamas AsyncTask.cancel(false)en un sistema operativo Android anterior, como Android 2.XX

Puedes elegir el mejor para ti.

Yueh-Ming Chien
fuente
0

Puede ser que inicialice pDialog globalmente, luego elimínelo e inicialice su vista o diálogo localmente. Tengo el mismo problema, lo he hecho y mi problema está resuelto. Espero que funcione para ti.

Shashwat Gupta
fuente
0

también hemos descartado nuestro diálogo sobre onPausemétodo o onDestroymétodo

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

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