【问题标题】:view.getViewTreeObserver().addOnGlobalLayoutListener leaks Fragmentview.getViewTreeObserver().addOnGlobalLayoutListener 泄漏 Fragment
【发布时间】:2015-03-31 14:18:37
【问题描述】:

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

我做什么:

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

仍然泄漏片段。

有没有人遇到过类似的问题并知道解决方法?

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

【问题讨论】:

  • 能否请您展示一下您的onDestroy 方法?
  • @marcel,我为您发布了一个可能的解决方案。

标签: android android-fragments memory-leaks


【解决方案1】:

我认为强烈删除 onDestroy() 中由 View 对象引用的 Listener 为时已晚。这个覆盖方法发生在onDestroyView() 之后,它应该“...清理与其视图关联的资源”。 您可以改用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() 的第二次调用,因为它在 JELLY_BEAN 之前不可用。
  • 如果您使用的是 Android Studio,则不需要编译指示代码 @SuppressWarnings("deprecation")
  • 代码myView = rootView.findViewById(R.id.myView); 可能需要更改为更适合您的应用或情况的代码。

【讨论】:

  • 这看起来是一个非常干净的解决方案,它成功地解决了我的内存泄漏问题。我不得不对代码进行一些调整/添加(我已经相应地编辑了你的答案)。但是,您能否确认我使用 int w = myView.getWidth(); 而不是 int w = viewTemp.getWidth(); 是正确的?事实上,你能告诉你为什么要使用viewTemp - 即,为什么不直接将myView 设为final?
  • @ban-geoengineering,我同意 myView 可以在发布的示例中声明为 final,更改它。但是在我的情况下,我无法直接更改声明,因为我使用了覆盖方法 getView()。
  • @marcel12345689 您应该选择这个作为正确答案(当然,如果它适合您)。
  • @ban-geoengineering,很高兴帮助一个如此大的贡献者,也许还有另一个开发人员。在帮助他人的同时获得帮助是正确的。继续下一个(新)问题:)
  • 为你的帮助干杯! :) 顺便说一句,达到 1,000 SO 积分是我一生中最伟大的时刻之一——甚至超过了我通过公路自行车能力测试的那一天。
【解决方案2】:

我遇到了同样的问题,但我通过删除 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();
    }

【讨论】:

  • 当您分析堆转储时,对您的片段的引用是否仍保留在树观察者的侦听器列表中?如果是这种情况,也许尝试保留对您最初从 getViewTreeObserver() 获得的对象的引用。也许不知何故它在 onCreate 和 onDestroy 之间被改变了?
  • @ban-geoengineering 你是否在 super.onDestroy() 之前调用了该方法?我相信可以通过 destroy 方法更改树观察者。
  • 是的,我试过了。我认为当到达 onDestroy() 时,树观察者不再活着。原始 Android 的答案已经对事情进行了排序,但也感谢您的帮助。
【解决方案3】:

好吧,也许这有点矫枉过正,但没有消息来源很难确定任何事情,所以试试这个。使您的 gLayoutListener 成为静态内部类(不保留对您的片段的强引用)。

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

【讨论】:

    【解决方案4】:

    您可以尝试创建自定义布局并将其作为根视图放置在您的 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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-02
        • 1970-01-01
        • 2017-10-26
        相关资源
        最近更新 更多