web-dev-qa-db-fra.com

view.getViewTreeObserver (). addOnGlobalLayoutListener fuit un fragment

Lorsque j'utilise la GlobalLayoutListener pour voir si le softKeyboard est ouvert ou non, le fragment n'est plus garbageCollected après sa destruction.

Ce que je fais:

  • Je retire l'auditeur dans le onDestroy() de mon fragment
  • Je règle l'auditeur sur null dans onDestroy()
  • Je règle la vue observée sur null dans onDestroy()

Fuit encore le fragment.

Quelqu'un a-t-il eu un problème similaire et connaît-il une solution?

Ma onDestroy:

   @Override
public void onDestroy(){
    Log.d(TAG , "onDestroy");

    if(Build.VERSION.SDK_INT < 16){
        view.getViewTreeObserver().removeGlobalOnLayoutListener(gLayoutListener);
    }else{
        view.getViewTreeObserver().removeOnGlobalLayoutListener(gLayoutListener);
    }

    view = null;
    gLayoutListener = null;



    super.onDestroy();
    }
12
marcel12345689

Je crois fermement que supprimer l'écouteur, référencé par un objet View, dans onDestroy () est trop tard. Cette méthode de substitution se produit après la variable onDestroyView (), supposée "nettoyer les ressources associées à sa vue" Vous pouvez utiliser le même code dans onStop() à la place. Bien que je n'ai pas utilisé cette technique.

Je peux suggérer ce code, que j'ai utilisé sans aucun problème avec le débogueur.

// Code below is an example. Please change it to code that is more applicable to your app.
final View myView = rootView.findViewById(R.id.myView);
myView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @SuppressLint("NewApi") @SuppressWarnings("deprecation")
    @Override
    public void onGlobalLayout() {

        // Obtain layout data from view...
        int w = myView.getWidth();
        int h = myView.getHeight();
        // ...etc.

        // Once data has been obtained, this listener is no longer needed, so remove it...
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            myView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        }
        else {
            myView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        }
    }
});

Remarques: 

  • Étant donné que getViewTreeObserver est utilisé pour les dispositions, vous n'avez normalement besoin de cet écouteur que pour une courte période. Par conséquent, l'auditeur est immédiatement supprimé.
  • Le second appel à removeOnGlobalLayoutListener () doit être barré par le studio car il n’est pas disponible avant JELLY_BEAN.
  • Le code Pragma @SuppressWarnings("deprecation") n'est pas nécessaire si vous utilisez Android Studio.
  • Le code myView = rootView.findViewById(R.id.myView); devra peut-être être remplacé par un code plus applicable à votre application ou à votre situation.
26

J'ai eu le même problème, mais je l'ai résolu en supprimant l'auditeur dans onDestroy (). Notez que la méthode à utiliser a été modifiée autour de JellyBean.

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
          mView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
    }

    @Override
    public void onDestroy() {       
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
            mView.getViewTreeObserver().removeGlobalOnLayoutListener(mGlobalLayoutListener);
        } else {
            mView.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
        }

        super.onDestroy();
    }
1
Jake Hall

C'est peut-être un peu excessif, mais il est difficile de dire quoi que ce soit sans sources, alors essayez ceci. Faites de votre gLayoutListener une classe interne statique (pour ne pas garder une référence forte à votre fragment). 

Si vous devez effectuer certaines opérations avec fragment ou ses champs dans listener, créez un WeakReference<YourFragment> dans le constructeur de votre classe de listener personnalisée et accédez à votre fragment par cette référence. N'oubliez pas de faire la vérification weakref.get() != null.

1
Dmide

J'ai également eu ce problème dans une vue personnalisée. J'ai enregistré une onPreDrawListener sur une vue enfant dans le constructeur de la vue personnalisée et je l'ai désenregistrée dans onDetachedFromWindow. La fuite de mémoire a persisté. Pour résoudre ce problème, j'ai tout essayé, mais j'ai finalement dû écrire un mécanisme alternatif non basé sur TreeObserver.

0
Mister Smith

Au lieu de l'utiliser, vous pouvez essayer de créer une disposition personnalisée et de la placer comme vue racine dans votre fichier xml.

class CustomLayout extends LinearLayout{
      public CustomLayout(Context context, AttributeSet attrs) {
             super(context, attrs);       
      } 

}

Puis substituez la méthode onsizechanged 

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    if (h < oldh) {
        // there is a difference, means keyboard is open.
    } else {

    }
}

Je suppose que votre application prend en charge un seul mode (Portrait ou Paysage).

Mettre à jour:

Tout faire dans 

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

}

Parce que je pense que vous initialisez auditeur dans onCreateView(), écouteur doit être supprimé dans onDestoryView(). onDestroy () sera appelé uniquement lorsque le fragment est détruit, pas lors du changement d'état.

Vérifiez le cycle de vie du fragment

0
droid kid