Vista non allegata all'arresto anomalo di Window Manager


190

Sto usando ACRA per segnalare arresti anomali delle app. Stavo ricevendo un View not attached to window managermessaggio di errore e pensavo di averlo corretto avvolgendo la pDialog.dismiss();in in un'istruzione if:

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

Ha ridotto la quantità di View not attached to window managercrash che ricevo, ma ne sto ancora ricevendo alcuni e non sono sicuro di come risolverlo.

Messaggio di errore:

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)

Snippet di codice:

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

Manifesto:

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

Cosa mi manca per fermare questo incidente?


1
L'hai mai capito? Sto avendo lo stesso problema. Non riesco a capirlo.
tomjung,

Sfortunatamente no. Potrei iniziare una taglia tra un po '. Dovresti dare un'occhiata ad alcuni degli altri thread che affrontano questo problema nel caso in cui ti aiutino.
Howli,

Il tuo AsyncTaskè dichiarato all'interno Activityo Fragment?
Erakitin,

Pubblica le funzioni "doMoreStuff ()" e "qualcosa ()".
berserk

Il problema potrebbe essere dovuto al troppo lavoro del thread principale, quindi prova a usare i gestori per mostrare il progressdialog, e se (pDialog! = Null) questa riga non è necessaria, perché isShowing stesso controlla se la finestra di dialogo è in corso o meno.
Madhu,

Risposte:


446

Come riprodurre il bug:

  1. Attivare questa opzione sul dispositivo: Settings -> Developer Options -> Don't keep Activities.
  2. Premere il pulsante Home mentre AsyncTaskè in esecuzione e ProgressDialogviene visualizzato.

Il sistema operativo Android distruggerà un'attività non appena viene nascosta. Quando onPostExecuteviene chiamato, Activitysarà nello stato "finitura" e ProgressDialognon verrà associato Activity.

Come sistemarlo:

  1. Controlla lo stato dell'attività nel tuo onPostExecutemetodo.
  2. Respingere il ProgressDialognel onDestroymetodo. Altrimenti, android.view.WindowLeakedverrà generata un'eccezione. Questa eccezione di solito proviene da finestre di dialogo ancora attive al termine dell'attività.

Prova questo codice fisso:

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

10
Bella risposta! A proposito, isShowing () non è necessario perché dismiss () non farà nulla se isShowing () == false. Codice sorgente
Peter Zhao,

3
Qual è il vantaggio di utilizzare isDestroyed()oltre isFinishing()a tutte le API per questo scopo specifico?
Alexander Abakumov,

@AlexanderAbakumov: da quello che capisco isFinishing()non è garantito trueche l'attività venga distrutta dal sistema, consultare la documentazione sulle attività .
Markus Penguin,

1
@MarkusPenguin: Esatto. Ma, in questo caso, se uno ha tentato di utilizzare un consiglio dal commento dell'autore // or call isFinishing() if min sdk version < 17, si imbatte nella stessa eccezione. Quindi, abbiamo bisogno di una soluzione diversa da questa risposta per le app in esecuzione su API <17.
Alexander Abakumov

@AlexanderAbakumov Sto riscontrando lo stesso problema, ma Finishing non funziona per me, ha funzionato salvando un riferimento debole alla mia attività all'interno di AsyncCallback e quindi:myActivityWeakReference.get() != null && !myActivityWeakReference.get().isFinishing()
rusito23

36

Il problema potrebbe essere che Activitysono stati finishedo dentro progress of finishing.

Aggiungi un segno di spunta isFinishinge chiudi la finestra di dialogo solo quando lo èfalse

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

isFinishing: controlla se questa attività è in fase di completamento, sia perché l'hai chiamata finisho qualcun altro ha richiesto che sia terminata.


1
Con il segno di spunta l'eccezione viene ancora lanciata
Marcos Vasconcelos il

Il mio ProgressDialog è in una classe che non è un'attività, quindi non riesco a usare il controllo isFinishing @Libin
Maniraj

12

Per Dialogcreato in un Fragment, uso il seguente codice:

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

Uso questo modello per trattare il caso in cui un Fragmentpuò essere staccato dal Activity.


8

Guarda come funziona il codice qui:

Dopo aver chiamato l'attività Asincrona, l'attività asincrona viene eseguita in background. è desiderabile. Ora, questa attività asincrona ha una finestra di dialogo di avanzamento che è collegata all'attività, se chiedi come vedere il codice:

pDialog = new ProgressDialog(CLASS.this);

Stai passando il Class.thiscontesto as all'argomento. Quindi la finestra di dialogo Progresso è ancora collegata all'attività.

Consideriamo ora lo scenario: se proviamo a terminare l'attività utilizzando il metodo finish (), mentre l'attività asincrona è in corso, è il punto in cui si sta tentando di accedere alla risorsa collegata all'attività, ovvero progress barquando l'attività non è più Là.

Quindi ottieni:

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

Soluzione a questo:

1) Assicurati che la finestra di dialogo sia chiusa o annullata prima che l'attività finisca.

2) Termina l'attività, solo dopo aver chiuso la finestra di dialogo, ovvero l'attività asincrona è terminata.


1
Per quanto riguarda # 2: non hai il controllo completo su quando terminare Activity; Android può finirlo in qualsiasi momento. Quindi, # 2 non ha senso.
Alexander Abakumov,

7

Basato sulla risposta di @erakitin, ma compatibile anche con le versioni di Android <livello API 17. Purtroppo Activity.isDestroyed () è supportato solo dal livello API 17, quindi se stai prendendo di mira un livello API più vecchio come me, dovrai controllalo tu stesso. Non ho avuto l' View not attached to window managereccezione dopo quello.

Codice di esempio

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

riferimento questo .


3

Sovrascrivi onConfigurationChanged e ignora la finestra di avanzamento. Se la finestra di dialogo di avanzamento viene creata in verticale e termina in orizzontale, verrà visualizzata la vista non allegata all'errore del gestore finestre.

Fermare anche la barra di avanzamento e interrompere l'attività asincrona nel metodo onPause (), onBackPressed e onDestroy.

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

    asyncTaskObj.cancel(true);

}

3

Sovrascrivi su Distruggi l'attività e Chiudi la finestra di dialogo e rendila nulla

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

3

In primo luogo, il motivo del crash è l'indice di decorView è -1, lo sappiamo dal codice sorgente di Android, c'è lo snippet di codice:

Classe: android.view.WindowManagerGlobal

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

quindi otteniamo la risoluzione del follow, giudichiamo solo l'indice di decorView, se più di 0 poi continua o semplicemente ritorna e rinuncia a scartare, codifica come segue:

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

Sostituisci il dismiss()metodo in questo modo:

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

Per riprodurre il problema, basta terminare l'attività prima di chiudere la finestra di dialogo.


1

soluzione migliore. Verificare che il primo contesto sia il contesto dell'attività o il contesto dell'applicazione se il contesto dell'attività viene verificato solo se l'attività è terminata oppure no, quindi chiamare dialog.show()odialog.dismiss();

Vedi il codice di esempio qui sotto ... spero che sia utile!

Visualizza finestra di dialogo

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

Chiudi finestra di dialogo

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

Se si desidera aggiungere più controlli, aggiungere dialog.isShowing()o dialog !-nullutilizzare &&condizione.


0

Questo problema è dovuto al completamento dell'attività prima che venga chiamata la funzione di eliminazione. Gestire l'eccezione e controllare il registro ADB per il motivo esatto.

/**
     * 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

Ho un modo per riprodurre questa eccezione.

Io uso 2 AsyncTask. Uno fa compiti lunghi e un altro fa compiti brevi. Dopo breve compito completare, chiamata finish(). Quando un'attività lunga viene completata e chiama Dialog.dismiss(), si arresta in modo anomalo.

Ecco il mio codice di esempio:

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

Puoi provare questo e scoprire qual è il modo migliore per risolvere questo problema. Dal mio studio, ci sono almeno 4 modi per risolverlo:

  1. @ risposta di erakitin: chiama isFinishing()per verificare lo stato dell'attività
  2. @ Risposta di Kapé: imposta il flag per controllare lo stato dell'attività
  3. Utilizzare try / catch per gestirlo.
  4. Chiamata AsyncTask.cancel(false)in onDestroy(). Impedirà l'esecuzione dell'asynctask onPostExecute()ma eseguirà onCancelled()invece.
    Nota: onPostExecute()eseguirà comunque anche le chiamate AsyncTask.cancel(false)su sistemi operativi Android meno recenti, come Android 2.XX

Puoi scegliere quello migliore per te.


0

Può essere che inizializzi pDialog a livello globale, quindi rimuovilo e inizializza la vista o la finestra di dialogo localmente. Ho lo stesso problema, l'ho fatto e il mio problema è stato risolto. Spero che funzionerà per te.


0

abbiamo anche chiuso la nostra finestra di dialogo sul onPausemetodo o sul onDestroymetodo

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

@Override
protected void onDestroy() {
    super.onDestroy();
    dialog.dismiss();
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.