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();
}
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.
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();
}
Well, maybe it's a bit overkill, but it's hard to tell anything for sure without sources, so try this. Make your gLayoutListener
a static inner class (to not keep a strong reference to your fragment).
If you need to perform some operations with fragment or it's fields inside listener, create a WeakReference<YourFragment>
inside the constructor of your custom listener class and access your fragment through this reference. Don't forget to do the check weakref.get() != null
.
Instead of using this, you can try creating a Custom Layout and place this as your root view in your xml.
class CustomLayout extends LinearLayout{
public CustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
Then override the onsizechanged method
@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 {
}
}
I am under the assumption that your application supports only one mode (Portrait or Landscape)
Update:
Do everything in
@Override
public void onDestroyView(){
super.onDestroyView();
}
Because I think you are initializing listener in onCreateView()
, so listener should be removed in onDestoryView()
. onDestroy() will be called only when the fragment is destroyed, not during state change.
Check the Fragment life cycle
I also had this problem in a custom view. I registered a onPreDrawListener
over a child view in the custom view constructor, and de-registered it in onDetachedFromWindow
. The memory leak persisted. To solve it I tried everything, but in the end I had to write an alternative mechanism not based on TreeObserver.