Утечка памяти при обращении к интерфейсу - PullRequest
0 голосов
/ 30 октября 2018

У меня вопрос об утечке памяти. У меня есть два класса.

Первый:

public class Utility {
private static Utility instance = null;
private UpdateListener listener;

//Make it a Singleton class
private Utility(){}
public static Utility getInstance() {
    if (instance == null)
        instance = new Utility();
    return instance;
}

public void setListener(UpdateListener listener) {
    this.listener = listener;
}

//Long running background thread
public void startNewTread() {
    new Thread (new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(1000 * 10);
                if (listener != null)
                    listener.onUpdate();
            } catch (InterruptedException e) {
                Log.d("Utility", e.getMessage());
            }
        }
    }).start();
}

//Listener interface
public interface UpdateListener {
    public void onUpdate();
}
}

Второй класс:

public class ListenerLeak extends AppCompatActivity {

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

    //Setting the listener
    Utility.getInstance().setListener(new Utility.UpdateListener() {
        @Override
        public void onUpdate() {
            Log.d("ListenerLeak", "Something is updated!");
        }
    });

    //Starting a background thread
    Utility.getInstance().startNewTread();
}

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

в этом упражнении. Может ли новый Utility.UpdateListener создать утечку памяти? когда действие уничтожено, только Updatelistener может быть живым. Может ли действие быть живым?

Ответы [ 2 ]

0 голосов
/ 31 октября 2018

Утечка просто означает, что объект класса больше не используется, но система не может восстановить память, в которой они живут (куча памяти). Это происходит потому, что существует хотя бы одна сильная ссылка, сохраняющая ссылку на объект.

Вернуться к вашему делу.

// Setting the listener
Utility.getInstance().setListener(new Utility.UpdateListener() {
    @Override
    public void onUpdate() {
        Log.d("ListenerLeak", "Something is updated!");
    }
});

Вы создаете анонимный внутренний класс (класс без имени), который реализует интерфейс Utility.UpdateListener, и вы также создаете анонимный объект анонимного внутреннего класса, используя ключевое слово new, но не сохраняете никаких ссылок на объект.

В Java, когда вы объявляете внутренний класс, он сохраняет ссылку на внешний класс (в данном случае ListenerLeak активность).

// Starting a background thread
Utility.getInstance().startNewTread();

В методе startNewTread вы создаете и запускаете фоновый поток.

new Thread (new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(1000 * 10);
            if (listener != null)
                listener.onUpdate();
        } catch (InterruptedException e) {
            Log.d("Utility", e.getMessage());
        }
    }
}).start();

Снова в этом классе вы создаете анонимный класс, который реализует интерфейс Runnable, и вы создаете анонимный объект класса Thread. В результате поток сохраняет ссылку на класс Utility. Это ссылочная цепочка

Background Thread -> Utility -> Listener -> ListenerLeak (the activity)

Когда пользователи нажимают кнопку возврата на активности. В это время активность используется дольше, и система может восстановить память, в которой она находится. К сожалению, потому что фоновый поток все еще хранит ссылку на него, поэтому система не может восстановить память. В результате происходит утечка.

Решение: Когда пользователи нажимают клавишу возврата, прежде чем уничтожить действие, система вызовет метод onDestroy. В этом методе вы должны удалить ссылку из фонового потока на это действие.

@Override
protected void onDestroy() {
    Utility.getInstance().setListener(null);
    super.onDestroy();
}

Вот цепочка ссылок

Background Thread -> Utility -> Listener -> null (point to nothing)

И теперь нет строгих ссылок на действия, поэтому система восстановит память, и утечки не будет.

0 голосов
/ 30 октября 2018

Создайте внутренний класс внутри класса Utility, как показано ниже. Затем переместите поток в этот класс.

 public void startNewTread() {
    new MyThread().start();
    }

 private static class MyThread extends Thread {
    @Override
    public void run() {
       try {
            Thread.sleep(1000 * 10);
            if (listener != null)
                listener.onUpdate();
        } catch (InterruptedException e) {
            Log.d("Utility", e.getMessage());
        }
    }
  }

Причина: после каждого изменения конфигурации система Android создает новую активность и оставляет старую для сборки мусора. Тем не менее, поток содержит неявную ссылку на старую активность и предотвращает ее восстановление. В результате каждая новая активность просачивается, и все связанные с ними ресурсы никогда не могут быть возвращены. https://www.androiddesignpatterns.com/2013/04/activitys-threads-memory-leaks.html поможет это понять.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...