Passing Context to ArrayAdapter inside Fragment with setRetainInstance(true) will cause leak?

12,722

Solution 1

The Fragment will be retained (and thus won't be garbage collected). The Fragment will hold a reference to the adapter, and the adapter holds a reference to the activity Context, so yes, I believe this will cause a memory leak.

A very simple solution would be to pass getActivity().getApplicationContext() to the adapter constructor instead.

Solution 2

Depending on what you are using the activity context for it may be possible to use the application context instead, but there are some circumstances where you may still require the activity context. You cannot, for instance, do a findViewById or display a toast/dialog with an application context.

If you must use the activity context, then I would add a method to your adapter for setting the context so you can set it (the context) to null on detach, then set it again when your fragment/activity is recreated.

Here's a good summary of the different context types and their capabilities: http://www.doubleencore.com/2013/06/context/

Share:
12,722
Mayank Mehta
Author by

Mayank Mehta

Updated on July 23, 2022

Comments

  • Mayank Mehta
    Mayank Mehta almost 2 years

    I have a ListFragment which would show list of items via an ArrayAdapter, I'm trying to handle configuration change (Device Rotation) I feel passing activity context to Array Adapter might cause Memory Leak when Activity is restarted on rotation and ListFragment adapter is retained because i'm using setRetainInstance(true), can someone tell me if my understanding is true? If so what is the best way to handle this. And yes I don't want to null my adapter onDetach and reuse it once Fragment view is re-created.

        public class DummyXListFragment extends RoboSherlockListFragment{
    
            @Override
            public void onCreate(Bundle savedInstanceState) {   
                 super.onCreate(savedInstanceState);
                 setRetainInstance(true);   
             }   
    
             @Override
             public void onActivityCreated(Bundle savedInstanceState) {
                 super.onActivityCreated(savedInstanceState);
    
    
                if (adapter == null)
                adapter = new DummyItemAdapter(getActivity(),
                    android.R.layout.simple_list_item_1, list);
    
        }
    
  • Mayank Mehta
    Mayank Mehta over 10 years
    But since Adapter shouldn't live more than activity passing it applicationContext wont cause an issue?
  • Alex Lockwood
    Alex Lockwood over 10 years
    The fragment is retained and therefore outlives the activity. So if the fragment holds a reference to the adapter, it will force the adapter to outlive the activity as well. And if the adapter holds a reference to the activity, that activity will never be reclaimed and a memory leak will occur.
  • Mayank Mehta
    Mayank Mehta over 10 years
    Yeah that is what is troubling me ... I feel re-creating the adapter is better alternative, what say?
  • Alex Lockwood
    Alex Lockwood over 10 years
    I don't see why that should be necessary. Just pass it the application context. There's no reason why the adapter should need to be linked directly to the activity lifecycle if it is only used inside the fragment. :)
  • Mayank Mehta
    Mayank Mehta over 10 years
    As you rightly pointed out it won't make sense to link it with activity lifecycle ..Can you clear this for me: Since Adapter would hold reference to Application Context it would remain in memory as long as Application is alive, right? Thinking on those lines I thought about re-creating the adapter (may be i can live with that as far memory foot print wont cause me an issue)
  • Alex Lockwood
    Alex Lockwood over 10 years
    The application context exists for the entirety of the application's lifetime, so there will be no memory leak. You should only need to create the adapter once when the retained fragment is first created... you definitely don't need to recreate the adapter at any time if you pass it the application context.
  • Martin Marconcini
    Martin Marconcini over 10 years
    You might need an activity context if you plan on doing view inflation (and want to preserve the correct Theme/Style) as correctly pointed by goto10 in his answer below.
  • Alex Lockwood
    Alex Lockwood over 10 years
    @MartínMarconcini Yes, but the array adapter never needs to do this, right?
  • Alex Lockwood
    Alex Lockwood over 10 years
    @MartínMarconcini Oops, I misread your comment. Yes, the array adapter will need to inflate views obviously, so you may need to pass the activity context instead.
  • Martin Marconcini
    Martin Marconcini over 10 years
    Yep, it's ok if you don't need it, but for adapters with getView methods, you might experience strange behavior if you don't have the right context (happened to me) ;)
  • lilbyrdie
    lilbyrdie about 10 years
    So it would seem a better solution might be to hold on to a weak reference of the activity in the adapter for inflating purposes and update the reference during onactivitycreated.
  • RoundSparrow hilltx
    RoundSparrow hilltx almost 10 years
    I'm not sure I have understood all the options presented in this discussion... but I wanted to share something recently discovered: that a View itself is context ;) you can use view.getContext() - and that way it's going to be routed to whoever was the original source. That way even if multiple Activities share a common ArrayAdapter - the origin of the view will be self context.