13

我阅读了许多关于如何避免 Android 中的内存泄漏的文章,但我仍然不太确定我是否正确。

  1. 我的应用程序由单个Activity.
  2. 我在那个 Activity 中没有任何私有或静态成员,所有代码都是从onCreate().
  3. 有一些自包含的静态类,它们的静态实例有时包含对 a ContextViews 的引用。在我的onDestroy()方法中,我将所有这些实例都设置为空。
  4. 我回收了我所有的Bitmaps。

Q1:够了吗?

让我感到困惑的是您可以在网上找到的经典示例(http://www.curious-creature.org/2008/12/18/avoid-memory-leaks-on-android/):

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

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

  setContentView(label);
}

我认为,一旦onCreate完成,label就会超出范围并被 GCed。

Q2:这怎么会造成内存泄漏?

我的活动基本上是这样的:

@Override
public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    /* Statics */
    AssetUtils.initIndex(this);
    BitmapLoader.startInstance(this);

    /* frame */
    ViewGroup frame = (ViewGroup) getLayoutInflater().inflate(R.layout.frame, null);
    this.setContentView(frame);

    /* create controller */
    Controller controller = new Controller(frame, getLayoutInflater());

    /* START */
    controller.start();
}

@Override
public void onDestroy() {
    super.onStop();

    /* Statics */
    AssetUtils.destroyInstance();
    BitmapLoader.destroyInstance();
}

在里面Controller,我偶尔会检索一个ContextusingView#getContext()以将其传递给手动创建View的 s 等。它永远不会静态存储在某个地方,只会存储在所有返回到Controller.

Q3:有什么我忽略的吗?

4

1 回答 1

23

Q1。你已经断章取义了(没有开玩笑的意思:)

如果您查看原始文章,泄漏实际上发生在引入 Bitmap 字段的下一个代码片段中。Roman 然后清楚地解释了为什么它会泄漏。您显示的代码不会泄漏。

http://www.curious-creature.org/2008/12/18/avoid-memory-leaks-on-android/

Q2。仅在没有其他选择的情况下使用 Activity 上下文,并且绝不允许在其范围大于其引用的 Activity 范围的范围内对其进行引用。据我所知,您显示的代码没有泄漏,因为没有任何上下文引用的范围大于您的 Activity。你怀疑它有吗?

Q3。使用位图时,无论是否使用静态引用,我都习惯于在 onPause() 中从视图中取消绑定 Bitamps(请记住 onDestroy() 不能保证,但它有点无关紧要,就好像你被销毁了,你的进程被杀死了所以 GC不是问题)。链接的文章还解释了如何执行此操作。我已经将它作为我的任何活动处理位图的模板模式。

[编辑]

对不起,我刚查了。文章没有说明如何解除绑定。这是我使用的模式:

@Override
protected void onPause()
{
        super.onPause();

        unbindDrawables(findViewById(R.id.mainLayout));
        System.gc();
}


@Override
protected void onDestroy()
{
        super.onDestroy();

        unbindDrawables(findViewById(R.id.mainLayout));
        System.gc();
}

private void unbindDrawables(View view)
{
        if (view.getBackground() != null)
        {
                view.getBackground().setCallback(null);
        }
        if (view instanceof ViewGroup && !(view instanceof AdapterView))
        {
                for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++)
                {
                        unbindDrawables(((ViewGroup) view).getChildAt(i));
                }
                ((ViewGroup) view).removeAllViews();
        }
}

mainLayout是 Activity 布局的根视图。

我包括 onDestroy() 因为我可能会手动完成()我的活动。

笔记。调用 system.gc() 是一个复杂的主题,您可能想省略它。这里有一些很好的讨论,为什么它可能不是一件好事,主要与性能有关。但是,在我看来,当一个活动被销毁时,暗示现在是执行 GC 的好时机并没有什么坏处。不必要地调用它的低效率将丢失在销毁活动的开销中。

于 2013-02-07T19:50:41.103 回答