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

Почему утечка памяти Android из-за статического Drawable, если это обратный вызов reset?

Я просто следил за этой статьей о том, как избежать утечек памяти: блог разработчиков 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);
}

Говорят, что drawable имеет обратную ссылку на textview (и косвенно на активность); который будет сохранен при вращении - и, следовательно, утечке памяти.

Мой запрос заключается в том, что обратный вызов не будет reset при вращении - он получит верхний текст (который будет содержать новый контекст), следовательно, позволяя использовать предыдущие экземпляры текста/контекста, который должен быть GC.

EDIT. Ответы, которые я получаю, касаются того, как "решить" проблему - я этого не ищу! Пожалуйста, перечитайте запрос. Я добавляю более подробную информацию. Когда действие запускается, ссылки:

Drawable1 → TextView1 → Activity1

При повороте Activity1 и TextView1 уничтожаются, но не Drawable1

Drawable1 → TextView2 → Activity2

Это означает, что Activity1 и TextView1 могут быть GC'ed, поскольку ни один другой объект не ссылается на них. Итак, что происходит?

Неужели я ошибаюсь в этом понимании? Или может быть, что Drawable может иметь несколько просмотров как обратные вызовы? (Рассматривая исходный код, я не вижу список обратных вызовов для Drawable).

4b9b3361

Ответ 1

Если вы вращаете устройство, тот же класс MyActivity (или другое имя, которое вы ему дали) воссоздается, обратный вызов перезаписывается и утечка существует до следующего GC. Проблема заключается в том, что вы переходите к другому действию, сохраняя ссылку на старый. Сегодня это смягчается, потому что setCallback теперь сохраняет обратный вызов в WeakReference, как вы можете видеть в текущем графическом коде, но это была сильная ссылка один раз (найдите setCallback(Callback cb)). В любом случае, вы правы, если вы просто посмотрите на одно действие, обратный вызов будет reset после поворота.

(редактировать, абзац добавлен): Например: MainAcivity @1 - это первый экземпляр. Когда вы вращаетесь, он уничтожается и создается новый MainActivity @2. Сначала происходит утечка, но как только sDrawable переназначается, MainActivity @1 можно бесплатно собрать, и проблем нет. Теперь предположим, что вместо поворота вы переходите к SecondActivity. Теперь sDrawable предназначен только для MainActivity и по-прежнему содержит ссылку на MainActivity @2, поэтому он протекает.

Смотрите этот код:

package com.douglasdrumond.leaky;

import android.os.Bundle;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.widget.TextView;

public class MainActivity extends Activity {
    private static Drawable sBackground;

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

        TextView label = new TextView(this);
        System.gc();
        long memory = Runtime.getRuntime().totalMemory()
                - Runtime.getRuntime().freeMemory();
        label.setText("Memory: " + memory / 1024f);

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

        setContentView(label);
    }

}

Очевидно, что вращение не увеличивает использование памяти.