view.getViewTreeObserver()。addOnGlobalLayoutListener泄漏片段

时间:2015-02-01 15:06:32

标签: android android-fragments memory-leaks

当我使用GlobalLayoutListener查看softKeyboard是否被打开时,片段在被销毁后不再是garbageCollected。

我的所作所为:

  • 我删除了片段onDestroy()中的监听器
  • 我将监听器设置为null
  • 中的onDestroy()
  • 我在onDestroy()
  • 中将观察到的视图设置为null

仍在泄漏碎片。

有没有人遇到过类似的问题并知道修复它?

我的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();
    }

5 个答案:

答案 0 :(得分:28)

我认为在onDestroy()中强烈删除View对象引用的Listener为时已晚。这种覆盖方法发生在onDestroyView()之后,它应该&#34; ...清理与其View相关的资源。&#34; 您可以在onStop()中使用相同的代码。虽然我没有使用这种技术。

我可以建议使用这个代码,我使用它而没有调试器的任何问题。

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

注意:

  • 由于getViewTreeObserver用于布局,通常只需短时间内就可以使用此监听器。因此,立即删除了听众。
  • removeOnGlobalLayoutListener()的第二次调用应由Studio划掉,因为它在JELLY_BEAN之前不可用。
  • 如果您正在使用Android Studio,则无需使用Pragma代码@SuppressWarnings("deprecation")
  • 代码myView = rootView.findViewById(R.id.myView);可能需要更改为适用于您的应用或情况的更适用的代码。

答案 1 :(得分:1)

我有同样的问题,但我通过删除onDestroy()中的侦听器来解决它。请注意在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();
    }

答案 2 :(得分:1)

嗯,也许这有点矫枉过正,但如果没有消息来源就很难说清楚,所以试试吧。让你的gLayoutListener成为一个静态的内部类(不要强烈引用你的片段)。

如果您需要在侦听器内使用片段或其字段执行某些操作,请在自定义侦听器类的构造函数内创建WeakReference<YourFragment>,并通过此引用访问您的片段。不要忘记检查weakref.get() != null

答案 3 :(得分:0)

您可以尝试创建自定义布局,并将其作为根视图放在xml中,而不是使用它。

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

}

然后覆盖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 {

    }
}

我假设您的应用程序仅支持一种模式(纵向或横向)

<强>更新

中做所有事情
@Override
public void onDestroyView(){
     super.onDestroyView();

}

因为我认为您正在onCreateView()中初始化侦听器,所以应该在onDestoryView()中删除侦听器。只有在片段被销毁时才会调用onDestroy(),而不是在状态更改期间调用。

检查Fragment life cycle

答案 4 :(得分:0)

我也在自定义视图中遇到此问题。我在自定义视图构造函数中的子视图上注册了onPreDrawListener,并在onDetachedFromWindow中取消注册了它。内存泄漏持续存在。为了解决这个问题,我尝试了所有方法,但最后我必须编写一个不基于TreeObserver的替代机制。