Подтвердить что ты не робот

Правильный onDestroy()/Как избежать утечек памяти

Я прочитал ряд статей о том, как избежать утечек памяти в Android, но я все еще не совсем уверен, правильно ли я понял.

  • Мое приложение состоит из одного Activity.
  • У меня нет каких-либо частных или статических членов в этом Activity, весь код запускается из onCreate().
  • In имеют некоторые автономные статические классы, чьи статические экземпляры иногда содержат ссылки на Context или View s. В моем методе onDestroy() я установил для всех этих экземпляров значение null.
  • Я перерабатываю все мои Bitmap s.

Q1: Этого достаточно?

То, что меня смущает, - это классический пример не-go, который вы можете найти в сети (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 я иногда извлекаю Context с помощью View#getContext(), чтобы передать его в созданное вручную View и тому подобное. Он никогда не хранится статически где-то, только в переменных-членах классов, все они возвращаются к Controller.

Q3: Есть ли что-то, что я забыл?

4b9b3361

Ответ 1

Q1. Вы выбрали это из контекста (никакой шуткой не предназначено:)

Если вы видите исходную статью, утечка фактически возникает в следующем фрагменте кода, где вводится поле битмапа. Затем Роман ясно объясняет, почему он течет. Код, который вы указали, НЕ просачивается.

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

Q2. Используйте контекст действия только там, где другого выбора нет, и НИКОГДА не допускайте ссылки на него в чем-то с областью действия, большей чем область действия, которую она ссылается. Код, который вы указали, не течет, насколько я вижу, поскольку ничто не имеет ссылки на контекст с областью больше, чем ваша активность. Вы подозреваете, что это так?

Q3. При использовании растровых изображений, статических ссылок или нет, у меня есть привычка к развязыванию Bitamps из Views in onPause() (помните, что onDestroy() не гарантируется, но это не имеет никакого отношения, как будто вы уничтожены, ваш процесс убит, так что GC это не проблема). Связанная статья также объясняет, как это сделать. Я создал шаблон шаблона для любой из моих операций с растровыми изображениями.

[EDIT]

Извините, я только что проверил. В статье не показано, как развязать. Вот шаблон, который я использую:

@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 - это корневой вид макета активности.

Я включаю onDestroy(), так как я могу вручную завершить() мою активность.

Примечание. Вызов system.gc() - сложный объект, и вы можете хотеть его пропустить. Здесь есть несколько хороших дискуссий, почему это может быть нехорошо, особенно в отношении производительности. Однако, на мой взгляд, когда действие уничтожается, намек на то, что сейчас самое время выполнить GC, не наносит вреда. Неэффективность вызова его без необходимости будет потеряна в накладных расходах на уничтожение активности.