La vue android n'est pas associée au gestionnaire de fenêtres

111

J'ai certaines des exceptions suivantes:

java.lang.IllegalArgumentException: View not attached to window manager
at android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:355)
at android.view.WindowManagerImpl.updateViewLayout(WindowManagerImpl.java:191)
at android.view.Window$LocalWindowManager.updateViewLayout(Window.java:428)
at android.app.Dialog.onWindowAttributesChanged(Dialog.java:596)
at android.view.Window.setDefaultWindowFormat(Window.java:1013)
at com.android.internal.policy.impl.PhoneWindow.access$700(PhoneWindow.java:86)
at com.android.internal.policy.impl.PhoneWindow$DecorView.drawableChanged(PhoneWindow.java:1951)
at com.android.internal.policy.impl.PhoneWindow$DecorView.fitSystemWindows(PhoneWindow.java:1889)
at android.view.ViewRoot.performTraversals(ViewRoot.java:727)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1633)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4338)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
at dalvik.system.NativeStart.main(Native Method)

Je l'ai googlé et j'ai vu que cela avait quelque chose à voir avec les popups et la rotation de l'écran, mais il n'y a aucune référence à mon code.

Les questions sont:

  1. existe-t-il un moyen de savoir exactement quand ce problème se produit?
  2. autre que de tourner l'écran, y a-t-il un autre événement ou action qui déclenche cette erreur?
  3. comment éviter que cela se produise?
Daniel Benedykt
la source
1
Voyez si vous pouvez expliquer comment l'activité est décrite dans le manifeste et quelle activité est affichée à l'écran lorsque l'erreur se produit. Voyez si vous pouvez résoudre votre problème dans un cas de test minimal.
Josh Lee
Peut-être que vous essayez de modifier votre vue avant d' View#onAttachedToWindow()avoir été appelée?
Alex Lockwood

Réponses:

163

J'ai eu ce problème où, lors d'un changement d'orientation de l'écran, l'activité s'est terminée avant la tâche AsyncTask avec la boîte de dialogue de progression terminée. J'ai semblé résoudre ce problème en définissant la boîte de dialogue sur null onPause(), puis en vérifiant cela dans AsyncTask avant de rejeter.

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

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

... dans ma AsyncTask:

protected void onPreExecute() {
    mDialog = ProgressDialog.show(mContext, "", "Saving changes...",
            true);
}

protected void onPostExecute(Object result) {
   if ((mDialog != null) && mDialog.isShowing()) { 
        mDialog.dismiss();
   }
}
johnnyblizzard
la source
12
@YekhezkelYovel l'AsyncTask s'exécute dans un thread qui survit au redémarrage de l'activité et peut contenir des références à l'activité maintenant morte et à ses vues (il s'agit en fait d'une fuite de mémoire, bien que probablement à court terme - dépend du temps que la tâche prend pour terminer ). La solution ci-dessus fonctionne bien, si vous êtes heureux d'accepter une fuite de mémoire à court terme. L'approche recommandée consiste à annuler () tout AsyncTask en cours d'exécution lorsque l'activité est suspendue.
Stevie
8

Après un combat avec ce problème, je me retrouve enfin avec cette solution de contournement:

/**
 * Dismiss {@link ProgressDialog} with check for nullability and SDK version
 *
 * @param dialog instance of {@link ProgressDialog} to dismiss
 */
public void dismissProgressDialog(ProgressDialog dialog) {
    if (dialog != null && 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 (!((Activity) context).isFinishing() {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                        if (!((Activity) context).isDestroyed()) {
                            dismissWithExceptionHandling(dialog);
                        }
                    } else { 
                        // Api < 17. Unfortunately cannot check for isDestroyed()
                        dismissWithExceptionHandling(dialog);
                    }
                }
            } else
                // if the Context used wasn't an Activity, then dismiss it too
                dismissWithExceptionHandling(dialog);
        }
        dialog = null;
    }
}

/**
 * Dismiss {@link ProgressDialog} with try catch
 *
 * @param dialog instance of {@link ProgressDialog} to dismiss
 */
public void dismissWithExceptionHandling(ProgressDialog dialog) {
    try {
        dialog.dismiss();
    } catch (final IllegalArgumentException e) {
        // Do nothing.
    } catch (final Exception e) {
        // Do nothing.
    } finally {
        dialog = null;
    }
}

Parfois, une bonne gestion des exceptions fonctionne bien s'il n'y avait pas de meilleure solution pour ce problème.

blueware
la source
5

Si vous avez un Activityobjet qui traîne, vous pouvez utiliser la isDestroyed()méthode:

Activity activity;

// ...

if (!activity.isDestroyed()) {
    // ...
}

C'est bien si vous avez une sous- AsyncTaskclasse non anonyme que vous utilisez à divers endroits.

Shawkinaw
la source
3

J'utilise une classe statique personnalisée qui crée et masque une boîte de dialogue. cette classe est utilisée par d'autres activités non seulement une seule activité. Maintenant, le problème que vous avez décrit m'est également apparu et j'ai passé la nuit pour trouver une solution.

Enfin je vous présente la solution!

si vous souhaitez afficher ou fermer une boîte de dialogue et que vous ne savez pas quelle activité a déclenché la boîte de dialogue afin de la toucher, le code suivant est pour vous.

 static class CustomDialog{

     public static void initDialog(){
         ...
         //init code
         ...
     }

      public static void showDialog(){
         ...
         //init code for show dialog
         ...
     }

     /****This is your Dismiss dialog code :D*******/
     public static void dismissProgressDialog(Context context) {                
            //Can't touch other View of other Activiy..
            //http://stackoverflow.com/questions/23458162/dismiss-progress-dialog-in-another-activity-android
            if ( (progressdialog != null) && progressdialog.isShowing()) {

                //is it the same context from the caller ?
                Log.w("ProgressDIalog dismiss", "the dialog is from"+progressdialog.getContext());

                Class caller_context= context.getClass();
                Activity call_Act = (Activity)context;
                Class progress_context= progressdialog.getContext().getClass();

                Boolean is_act= ( (progressdialog.getContext()) instanceof  Activity )?true:false;
                Boolean is_ctw= ( (progressdialog.getContext()) instanceof  ContextThemeWrapper )?true:false;

                if (is_ctw) {
                    ContextThemeWrapper cthw=(ContextThemeWrapper) progressdialog.getContext();
                    Boolean is_same_acivity_with_Caller= ((Activity)(cthw).getBaseContext() ==  call_Act )?true:false;

                    if (is_same_acivity_with_Caller){
                        progressdialog.dismiss();
                        progressdialog = null;
                    }
                    else {
                        Log.e("ProgressDIalog dismiss", "the dialog is NOT from the same context! Can't touch.."+((Activity)(cthw).getBaseContext()).getClass());
                        progressdialog = null;
                    }
                }


            }
        } 

 }
aimiliano
la source
1

La solution ci-dessus n'a pas fonctionné pour moi. Donc, ce que j'ai fait, c'est prendre ProgressDialogcomme global et ensuite l'ajouter à mon activité

@Override
    protected void onDestroy() {
        if (progressDialog != null && progressDialog.isShowing())
            progressDialog.dismiss();
        super.onDestroy();
    }

de sorte que dans le cas où l'activité est détruite, ProgressDialog sera également détruit.

Kishan Solanki
la source
0

Pour la question 1):

Étant donné que le message d'erreur ne semble pas indiquer quelle ligne de votre code est à l'origine du problème, vous pouvez le localiser en utilisant des points d'arrêt. Les points d'arrêt interrompent l'exécution du programme lorsque le programme atteint des lignes de code spécifiques. En ajoutant des points d'arrêt aux emplacements critiques, vous pouvez déterminer quelle ligne de code provoque le plantage. Par exemple, si votre programme plante sur une ligne setContentView (), vous pouvez y placer un point d'arrêt. Lorsque le programme s'exécute, il se met en pause avant d'exécuter cette ligne. Si la reprise provoque le blocage du programme avant d'atteindre le point d'arrêt suivant, vous savez alors que la ligne qui a tué le programme se trouvait entre les deux points d'arrêt.

L'ajout de points d'arrêt est facile si vous utilisez Eclipse. Faites un clic droit dans la marge juste à gauche de votre code et sélectionnez "Toggle breakpoint". Vous devez ensuite exécuter votre application en mode débogage, le bouton qui ressemble à un insecte vert à côté du bouton d'exécution normal. Lorsque le programme atteint un point d'arrêt, Eclipse passe à la perspective de débogage et vous montre la ligne à laquelle il attend. Pour redémarrer le programme, recherchez le bouton «Reprendre», qui ressemble à un «Lecture» normal mais avec une barre verticale à gauche du triangle.

Vous pouvez également remplir votre application avec Log.d ("Mon application", "Quelques informations ici qui vous indiquent où se trouve la ligne du journal"), qui publie ensuite des messages dans la fenêtre LogCat d'Eclipse. Si vous ne trouvez pas cette fenêtre, ouvrez-la avec Fenêtre -> Afficher la vue -> Autre ... -> Android -> LogCat.

J'espère que cela pourra aider!

Steve Haley
la source
7
Le problème se produit sur les téléphones des clients, je n'ai donc pas d'option de débogage. De plus, le problème se produit tout le temps, arrive parfois, alors je ne sais pas comment le suivre
Daniel Benedykt
Vous pouvez toujours recevoir des messages de débogage à partir d'un téléphone, si vous pouvez en mettre la main sur un. Dans le menu -> paramètres -> applications -> développement, il y a une option pour le débogage USB. Si activé, vous pouvez ensuite brancher le téléphone et LogCat attrape toutes les lignes de débogage normales. A part ça ... eh bien ... l'intermittence pourrait s'expliquer par elle en fonction de l'état du programme. Je suppose que vous devrez jouer avec le programme pour voir si vous pouvez recréer le problème.
Steve Haley
4
Comme je l'ai déjà dit, le problème se produit sur un téléphone client et je n'y ai pas accès. Le client est peut-être sur un autre continent :)
Daniel Benedykt
Il existe des applications sur le marché qui vous enverront leur logcat par e-mail.
Tim Green
1
Juste pour que vous sachiez que vous pouvez faire pivoter l'émulateur en appuyant sur la touche du pavé numérique 9
Rob
0

J'ai ajouté ce qui suit au manifeste de cette activité

android:configChanges="keyboardHidden|orientation|screenLayout"
Rickey
la source
19
Ce n'est pas une solution: le système pourrait détruire votre activité pour d'autres raisons également.
Roel
1
Pouvez-vous expliquer votre réponse s'il vous plaît?
Leebeedev
0

selon le code du windowManager (lien ici ), cela se produit lorsque la vue que vous essayez de mettre à jour (qui appartient probablement à une boîte de dialogue, mais pas nécessaire) n'est plus attachée à la racine réelle des fenêtres.

comme d'autres l'ont suggéré, vous devez vérifier l'état de l'activité avant d'effectuer des opérations spéciales sur vos boîtes de dialogue.

voici le code correspondant, qui est la cause du problème (copié à partir du code source Android):

public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }

    final WindowManager.LayoutParams wparams
            = (WindowManager.LayoutParams)params;

    view.setLayoutParams(wparams);

    synchronized (this) {
        int index = findViewLocked(view, true);
        ViewRootImpl root = mRoots[index];
        mParams[index] = wparams;
        root.setLayoutParams(wparams, false);
    }
}

private int findViewLocked(View view, boolean required) {
        synchronized (this) {
            final int count = mViews != null ? mViews.length : 0;
            for (int i=0; i<count; i++) {
                if (mViews[i] == view) {
                    return i;
                }
            }
            if (required) {
                throw new IllegalArgumentException(
                        "View not attached to window manager");
            }
            return -1;
        }
    }
développeur android
la source
le code que j'ai écrit ici est ce que fait google. ce que j'ai écrit est pour montrer la cause de ce problème.
développeur android
0

Mon problème a été résolu en bloquant la rotation de l'écran sur mon Android, l'application qui me causait un problème fonctionne maintenant parfaitement

Roger
la source
0

Une autre option consiste à ne pas démarrer la tâche asynchrone tant que la boîte de dialogue n'est pas attachée à la fenêtre en remplaçant onAttachedToWindow () dans la boîte de dialogue, de cette façon, elle est toujours rejetable.

Nick Palmer
la source
0

Ou simplement vous pouvez ajouter

protected void onPreExecute() {
    mDialog = ProgressDialog.show(mContext, "", "Saving changes...", true, false);
}

ce qui rendra le ProgressDialognon annulable

Sarith Vasu
la source
-2

Pourquoi ne pas essayer catch, comme ceci:

protected void onPostExecute(Object result) {
        try {
            if ((mDialog != null) && mDialog.isShowing()) {
                mDialog.dismiss();
            }
        } catch (Exception ex) {
            Log.e(TAG, ex.getMessage(), ex);
        }
    }
Heasen
la source
IllegalStateOfException Doit être traité d'une meilleure manière, plutôt juste pour se comporter anormalement.
Lavakush
-3

lorsque vous déclarez une activité dans le manifeste, vous avez besoin d'android: configChanges = "orientation"

exemple:

<activity android:theme="@android:style/Theme.Light.NoTitleBar" android:configChanges="orientation"  android:label="traducción" android:name=".PantallaTraductorAppActivity"></activity>
Cristian
la source
1
Cette balise est requise uniquement si le développeur ne souhaite pas détruire et recréer l'activité par le système Android.
Ankit