Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

view.getViewTreeObserver().addOnGlobalLayoutListener leaks Fragment

When I use the GlobalLayoutListener to see if the softKeyboard is opened or not the fragment is not garbageCollected anymore after it is destroyed.

What I do:

  • I remove the Listener in the onDestroy() of my Fragment
  • I set the Listener to null in onDestroy()
  • I set the view that is observed to null in onDestroy()

Still leaks the fragment.

Does anyone had a similar issue and knows a fix for it??

My 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();
    }
like image 905
marcel12345689 Avatar asked Feb 01 '15 15:02

marcel12345689


2 Answers

I believe strongly removing the Listener, referenced by a View object, in onDestroy() is too late. This override method occurs after onDestroyView(), which supposed to "...clean up resources associated with its View." You may use the same code in onStop() instead. Although I did not use this technique.

I can suggest this code, which I have used without any issues with the debugger.

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

Notes:

  • Since getViewTreeObserver is used for layouts, normally you need this listener for a short time only. Hence the listener is removed immediately.
  • The second call to removeOnGlobalLayoutListener() should be crossed out by the Studio since it is not available before JELLY_BEAN.
  • Pragma code @SuppressWarnings("deprecation") is not necessary if you're using Android Studio.
  • Code myView = rootView.findViewById(R.id.myView); may need to change to a more applicable code to your app or situation.
like image 155
The Original Android Avatar answered Sep 26 '22 06:09

The Original Android


I had this same issue, but I resolved it by removing the listener in onDestroy(). Note the method to use changed around 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();
    }
like image 28
Jake Hall Avatar answered Sep 22 '22 06:09

Jake Hall