Как полностью очистить элементы меню ActionBar/Toolbar и избежать утечек памяти?

У меня есть Activity с контейнером фрагментов. В зависимости от того, какой фрагмент в данный момент находится в контейнере, мне нужно динамически менять меню ActionBar'а и передать события этого меню текущему фрагменту. Для этого, я вызываю invalidateOptionsMenu() чтобы добиться вызова onCreateOptionsMenu() и выставляю соответствующие слушатели:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    menu.clear(); // Пытаюсь очистить существующим методом
    Fragment fragment = getSupportFragmentManager().findFragmentByTag(mCurrentFragmentTag);

    if (fragment instanceof PlayersView) {
        final PlayersView view = (PlayersView) fragment;
        getMenuInflater().inflate(R.menu.menu_main_players, menu);
        SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
        searchView.setOnCloseListener(new SearchView.OnCloseListener() {
            @Override
            public boolean onClose() {
                view.loadData(false);
                return false;
            }
        });
        return true;
    }

    return super.onCreateOptionsMenu(menu);
}

И всё работает неплохо, вот только проблема возникает, когда я переключаю фрагменты. Из-за этих Listener'ов происходит утечка фрагмента, которую отлавливает LeakCanary:
ad25f701a7fc4b41af384592762dea01.jpg
SearchView должен был быть выкинут и уничтожен сборщиком мусора, но вместо этого он находится в памяти и удерживает ссылку на фрагмент из-за Listener'а. Как избавиться от всех ссылок и избежать утечек памяти? По идее, Menu.clear() должен это делать, но на деле толку от метода ноль. Может можно как-то полностью пересоздать объект Menu, вместо переиспользования одной инстанции?
  • Вопрос задан
  • 254 просмотра
Решения вопроса 1
gim0
@gim0 Автор вопроса
Немного неудобно, но решил следующим образом. Во-первых, я перенёс логику метода onCreateOptionsMenu в сами фрагменты, чтобы не делать кучу проверок в Activity на то, какой фрагмент в данный момент находится в контейнере. Во-вторых, в фрагментах я сохраняю ссылку на SearchView и удаляю все слушатели самостоятельно:
private SearchView mActionSearch;

private void disposeActions() {
    if (mActionSearch != null) {
        mActionSearch.setOnQueryTextListener(null);
        mActionSearch.setOnCloseListener(null);
    }

    mActionSearch = null;
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    disposeActions();

    inflater.inflate(R.menu.menu_main_players, menu);
    mActionSearch = (SearchView) menu.findItem(R.id.action_search).getActionView();
    mActionSearch.setOnCloseListener(new SearchView.OnCloseListener() {
        @Override
        public boolean onClose() {
            loadData(false);
            return false;
        }
    });
    // ...
}

@Override
public void onDetach() {
    super.onDetach();
    disposeActions();
}

Возможно кому-то окажется полезным.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы