A
A
Andrey Myvrenik2017-03-27 11:22:24
Android
Andrey Myvrenik, 2017-03-27 11:22:24

How to completely clear ActionBar/Toolbar menu items and avoid memory leaks?

I have an Activity with a fragment container. Depending on which fragment is currently in the container, I need to dynamically change the ActionBar's menu and pass the events of this menu to the current fragment. To do this, I call invalidateOptionsMenu() to get onCreateOptionsMenu() called and set up the appropriate listeners:

@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);
}

And everything works well, only the problem arises when I switch fragments. Because of these Listeners, the fragment is leaked, which LeakCanary catches :
ad25f701a7fc4b41af384592762dea01.jpg
the SearchView should have been thrown and destroyed by the garbage collector, but instead it is in memory and holds a reference to the fragment because of the Listener. How to get rid of all references and avoid memory leaks? In theory, Menu.clear() should do this, but in reality, the method is of no use. Maybe you can somehow completely recreate the Menu object, instead of reusing one instance?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
A
Andrey Myvrenik, 2017-03-27
@gim0

A little inconvenient, but solved as follows. Firstly, I moved the logic of the onCreateOptionsMenu method to the fragments themselves, so as not to do a bunch of checks in the Activity for which fragment is currently in the container. Secondly, in the fragments, I keep a reference to the SearchView and remove all listeners myself:

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();
}

Perhaps someone will be useful.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question