Marcos Vasconcelos
Marcos Vasconcelos

Reputation: 18276

Memory issues - fragments

I recently refactored a application and replaced a ViewFlipper for a FrameLayout on which I swap between Fragments.

Each time user request one of the views:

    public void showLibraryOf(long publisherId) {
        library = new DownloadLibraryFragment(id, viewFactory());

        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.container, library);
        ft.commit();

        library.setAdapterObserver(this);
    }

    public void showMyLibraryOf(long publisherId) {
        myLibrary = new MyLibraryFragment(id, viewFactory());

        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.container, myLibrary);
        ft.commit();
    }

    public void showHelp() {
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.container, new HelpFragment());
        ft.commit();
    }

I create a new Fragment and replace the old one. Those being removed from screen get onDestroy called, but the memory consumed by the bitmaps I load on the screen does not get removed, so the application crashes after some swap between the fragments.

I also tried to remove references at onDestroy

@Override
public void onDestroyView() {
    destroy();
    super.onDestroyView();   
    adapter.clear();
    adapter.clearObservers();
    adapter.notifyDataSetChanged();

    view.setAdapter(new ArrayAdapter<Journal>(getActivity(), 0));

    adapter = null;
    view = null;
}

But the memory keeps growing.

Anyone knows any solution? maybe reuse fragments? effectively destroy it? I'm listening.

Upvotes: 4

Views: 9451

Answers (1)

dskinner
dskinner

Reputation: 10857

I forget which stackoverflow question I originally pulled this code from, but one method that seems to work well is to override onAttachFragment of the FragmentActivity, and then store a WeakReference to each fragment passed in. Then, instead of using the replace method of a FragmentTransaction, you recycle all the fragments (as relevant for the case).

Here's an example of additional members and methods on a FragmentActivity that creates a default fragment in onCreate and responds to changes via onNewIntent:

private List<WeakReference<Fragment>> mFragments = 
    new ArrayList<WeakReference<Fragment>>();

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.add(R.id.fragment_container, MyFragment.newInstance("default"));
    ft.commit();
}

@Override
protected void onNewIntent(Intent intent) {
    setIntent(intent);
    String section = intent.getStringExtra("section");
    recycleFragments();
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.add(R.id.fragment_container, MyFragment.newInstance(section));
    ft.commit();
}

@Override
public void onAttachFragment(Fragment fragment) {
    mFragments.add(new WeakReference<Fragment>(fragment));
}

private void recycleFragments() {
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();

    for (WeakReference<Fragment> ref : mFragments) {
        Fragment fragment = ref.get();
        if (fragment != null) {
            ft.remove(fragment);
        }
    }

    ft.commit();
}

Now if you monitor the heap, you should notice it's not blowing up in size. This solution mostly comes into play when you have nested fragments containing bitmaps which for some reason don't seem to get recycled properly. I'd love a more elegant solution but this one works.

Upvotes: 6

Related Questions