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

  • 0
    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 the call onCreateOptionsMenu () and set 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, a fragment is leaking, which is caught by LeakCanary :

    The SearchView should have been thrown and destroyed by the garbage collector, but instead it resides in memory and holds the fragment reference because of the Listener. How to get rid of all references and avoid memory leaks? In theory, Menu.clear () should do this , but in fact there is zero sense from the method. Maybe you can somehow completely re-create the Menu object, instead of reusing one instance?
    Android Anonymous, Mar 18, 2020

  • 1 Answers
  • 0
    A bit inconvenient, but I solved it as follows. Firstly, I transferred the logic of the onCreateOptionsMenu method to the fragments themselves, so as not to make a bunch of checks in the Activity for which fragment is currently in the container. Secondly, in the snippets, I keep the link to 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 it will be useful to someone.
    Anonymous

Your Answer
To place the code, please use CodePen or similar tool. Thanks you!