【问题标题】:Avoid memory leaks on Android避免Android上的内存泄漏
【发布时间】:2011-07-04 05:34:52
【问题描述】:

我刚刚阅读了a blogpost by Romain Guy,了解如何避免 Android 中的内存泄漏。

在文章中他举了这个例子:

private static Drawable sBackground;

@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);

  TextView label = new TextView(this);
  label.setText("Leaks are bad");

  if (sBackground == null) {
    sBackground = getDrawable(R.drawable.large_bitmap);
  }
  label.setBackgroundDrawable(sBackground);

  setContentView(label);
}

罗曼说:

这个例子是泄露上下文的最简单的例子之一。

我的问题是,你如何正确修改它?

就这样?

TextView label = new TextView(Context.getApplicationContext());

我测试了两种方法,结果是一样的。我找不到区别。而且我认为this 比应用程序上下文更正确。因为this是对Activity的引用,也就是说TextView属于那个Activity

有人能给我解释一下吗?

【问题讨论】:

  • 它会占用整个应用程序生命周期...

标签: android


【解决方案1】:

该代码的实际问题不是传递给创建drawable的上下文,而是私有静态Drawable sBackground; 静态 Drawable 是使用 Activity 作为上下文创建的,所以在这种情况下,有一个对引用 Activity 的 Drawable 的静态引用,这就是为什么会出现泄漏。只要该引用存在,Activity 就会保存在内存中,并泄漏其所有视图。

所以应该使用应用程序上下文创建 Drawable,而不是 TextView。用“this”创建 TextView 非常好。

edit :实际上,这可能没什么大不了的,问题是一旦可绘制对象绑定到视图,就会有对视图的引用,它引用了活动。因此,您需要在退出活动时“取消绑定”可绘制对象。

【讨论】:

  • 这是应该使用应用程序上下文创建的Drawable-->您认为应该是sBackground = getApplicationContext().getResources().getDrawable(R.drawable.icon);是吗?
  • @Judy 是的,那应该更好。
  • @Judy 实际上,不,这不应该有所不同,问题是当您将可绘制对象绑定到视图时,它会获得对它的引用,该引用包含对活动的引用。所以你实际上需要从视图中彻底解除可绘制对象的绑定。
  • "从视图中解绑drawable"-->如何从视图中解绑drawable?就像这段代码:`@Override protected void onDestroy() { // TODO 自动生成的方法存根 label.setBackgroundDrawable(null); super.onDestroy(); }' 请告诉我如何实现它。谢谢
  • 在您的可绘制对象上调用 setCallback(null)。
【解决方案2】:

我不确定 Romain 是否在您阅读后更新了他的博客条目,但他非常清楚如何避免泄漏,甚至为您指出了 Android 操作系统中的示例。请注意,我通过 archive.org 修复了 Romain 博客条目中损坏的链接。

这个例子是泄露 Context 和 您可以通过设置存储的 当 Activity 被销毁时,drawables 的回调为 null。 有趣的是,在某些情况下,您可以创建一个链 泄露的上下文,它们很糟糕。它们会让你内存不足 相当快。

有两种简单的方法可以避免与上下文相关的内存泄漏。这 最明显的一个是避免将上下文转义到它自己之外 范围。上面的示例显示了静态引用的情况,但是 内部类及其对外部类的隐式引用可以是 同样危险。第二种解决方案是使用应用程序 语境。只要您的应用程序存在,此上下文就会存在 并且不依赖于活动生命周期。如果你打算 保留需要上下文的长寿命对象,记住 应用对象。您可以通过拨打电话轻松获取 Context.getApplicationContext() 或 Activity.getApplication()。

总之,为了避免与上下文相关的内存泄漏,请记住 以下:

  • 不要保留对上下文活动的长期引用(对活动的引用应该与 活动本身)
  • 尝试使用上下文应用程序而不是上下文活动
  • 如果您不控制其生命周期,请避免在活动中使用非静态内部类,使用静态内部类并对内部活动进行弱引用。这个问题的解决方案是使用一个静态内部类和一个对外部类的 WeakReference,就像在 ViewRoot 和它的 W 内部类中所做的那样
  • 垃圾收集器不能防止内存泄漏

【讨论】:

    【解决方案3】:

    该代码的内存泄漏主要发生在您旋转屏幕(即更改方向状态)时,因此您的活动被销毁并为新方向再次创建。关于内存泄漏有很多解释。

    您可以观看有关内存管理here 的 Google I/O 2011 视频之一。在视频中,您还可以使用内存分析器等内存管理工具,可在here下载。

    【讨论】:

    • Setyadi 感谢您的意见。该资源对我非常有用。
    • 在不旋转屏幕的情况下可能会发生内存泄漏。
    • @IgorGanapolsky 这就是他说得最多的原因。
    【解决方案4】:

    我不知道您在应用程序中是否遇到任何问题,但我创建了一个解决方案,可以修复标准 android 类的所有 android 内存泄漏问题:http://code.google.com/p/android/issues/detail?id=8488#c51

    public abstract class BetterActivity extends Activity
    {
      @Override
      protected void onResume()
      {
        System.gc();
        super.onResume();
      }
    
      @Override
      protected void onPause()
      {
        super.onPause();
        System.gc();
      }
    
      @Override
      public void setContentView(int layoutResID)
      {
        ViewGroup mainView = (ViewGroup)
          LayoutInflater.from(this).inflate(layoutResID, null);
    
        setContentView(mainView);
      }
    
      @Override
      public void setContentView(View view)
      {
        super.setContentView(view);
    
        m_contentView = (ViewGroup)view;
      }
    
      @Override
      public void setContentView(View view, LayoutParams params)
      {
        super.setContentView(view, params);
    
        m_contentView = (ViewGroup)view;
      }
    
      @Override
      protected void onDestroy()
      {
        super.onDestroy();
    
        // Fixes android memory  issue 8488 :
        // http://code.google.com/p/android/issues/detail?id=8488
        nullViewDrawablesRecursive(m_contentView);
    
        m_contentView = null;
        System.gc();
      }
    
      private void nullViewDrawablesRecursive(View view)
      {
        if(view != null)
        {
          try
          {
            ViewGroup viewGroup = (ViewGroup)view;
    
            int childCount = viewGroup.getChildCount();
            for(int index = 0; index < childCount; index++)
            {
              View child = viewGroup.getChildAt(index);
              nullViewDrawablesRecursive(child);
            }
          }
          catch(Exception e)
          {          
          }
    
          nullViewDrawable(view);
        }    
      }
    
      private void nullViewDrawable(View view)
      {
        try
        {
          view.setBackgroundDrawable(null);
        }
        catch(Exception e)
        {          
        }
    
        try
        {
          ImageView imageView = (ImageView)view;
          imageView.setImageDrawable(null);
          imageView.setBackgroundDrawable(null);
        }
        catch(Exception e)
        {          
        }
      }
    
      // The top level content view.
      private ViewGroup m_contentView = null;
    }
    

    【讨论】:

    • 这真的有效吗?我认为活动被销毁和垃圾收集之间只有一些延迟。
    猜你喜欢
    • 1970-01-01
    • 2018-04-08
    • 2013-06-24
    • 1970-01-01
    • 1970-01-01
    • 2018-11-02
    • 1970-01-01
    相关资源
    最近更新 更多