Listview in Fragment is causing Memory Leak
<p>I have a FragmentActivity with a <code>FragmentMediaOverview</code> containing a list of <code>MediaItemView</code>s (each with a imageview and some text) and a click on one of the items opening a detail-Fragment.
Now when I go back (via back button) and forth (click on listitem) several times from list to detail fragment I eventually run into OOM-Errors. I use <code>SoftReference</code>s for the bitmaps in the listitems as well as in the detail fragment.
According to MAT there is an incresing number of <code>MediaItemView</code>s as well as <code>FragmentMediaOverview</code> instances, but I just cannot figure out why.</p>

<p>I read this <a href="http://stackoverflow.com/questions/7083441/android-alertdialog-causes-a-memory-leak/">Android: AlertDialog causes a memory leak</a> , but couldn't solve it <code>null</code>ing out listeners.</p>

<p>Here is my code:</p>

<p>FragmentMediaOverview.java</p>

<p>(This is not a ListFragment because for a tablet-layout the <code>MediaAdapter</code> needs to connect to a gridview)</p>

<pre><code>public class FragmentMediaOverview extends Fragment {
    private static String TAG = FragmentMediaOverview.class.getSimpleName();

    private MediaAdapter adapter;
    private OnMediaSelectedListener selListener;
    private ArrayList&lt;BOObject&gt; mediaItems;

    private ViewGroup layoutContainer;    
    private AdapterView itemContainer; // list or gridview

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView");
        layoutContainer = (ViewGroup) inflater.inflate(R.layout.fragment_media_overview, null);

        return layoutContainer;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        selListener = (OnMediaSelectedListener) activity;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        itemContainer.setOnItemClickListener(null);
        selListener = null;
        adapter = null;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        initUi(layoutContainer);
        displayMedia();
    }

    private void initUi(ViewGroup layoutContainer) {
        itemContainer = (AdapterView) layoutContainer.findViewById(android.R.id.list);
        itemContainer.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView&lt;?&gt; parent, View view, int position, long id) {
                BOMedia mediaItem = ((BOMedia) mediaItems.get(position));
//the FragmentActivity is coordinating the FragmentTransactions
                selListener.onMediaSelected(mediaItem);
            }
        });
    }

    private void displayMedia() {
        Log.d(TAG, "Displaying List");
        if (mediaItems == null) {
            loadMedia();
            return;
        }

        Log.d(TAG, "List: " + mediaItems.size() + ", adapter: " + itemContainer.getAdapter());

        if (adapter == null) {
            Log.d(TAG, "Create Adapter with " + mediaItems.size());

            adapter = new MediaAdapter(getActivity(), mediaItems);
        }

        if (itemContainer.getAdapter() == null) {
            itemContainer.setAdapter(adapter);
        } else {
            adapter.setItems(mediaItems);
            adapter.notifyDataSetChanged();
        }

    }

    private void loadMedia() {
        FragmentHelper.showProgressSpinner(layoutContainer, android.R.id.list);
        DbHelper.getInstance().getMedia(mediaType, new DbQueryFinishListener() {

            @Override
            public void onDbCallFinish(ArrayList&lt;BOObject&gt; objects) {
                if (!getActivity().isFinishing()) {
                    mediaItems = objects;
                    Collections.sort(mediaItems, new Comparator&lt;BOObject&gt;() {
                        final Collator c = Collator.getInstance(Locale.GERMAN);
                        @Override
                        public int compare(BOObject s1, BOObject s2) {
                            if (s2 != null &amp;&amp; ((BOMedia) s2).getTitle() != null &amp;&amp; s1 != null
                                    &amp;&amp; ((BOMedia) s1).getTitle() != null) {
                                return c.compare(((BOMedia) s1).getTitle(),((BOMedia) s2).getTitle());
                            } else {
                                return 0;
                            }
                        }
                    });
                    displayMedia();
                    FragmentHelper.hideProgressSpinner(layoutContainer, android.R.id.list);
                }
            }

            @Override
            public void onDbCallException(Exception exception) {
                if (!getActivity().isFinishing()) {
                    FragmentHelper.hideProgressSpinner(layoutContainer, android.R.id.list);
                }
            }
        });

    }
}
</code></pre>

<p>MediaAdapter.java</p>

<pre><code>public class MediaAdapter extends BaseAdapter {
    private static final String TAG = MediaAdapter.class.getSimpleName();
    private Context context;
    private ArrayList&lt;BOObject&gt; mediaItems;

    public MediaAdapter(Context c, ArrayList&lt;BOObject&gt; mediaItems) {
        super();
        context = c;
        this.mediaItems = mediaItems;
    }

    @Override
    public int getCount() {
        return mediaItems.size();
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = new MediaItemView(context);
        }
        ((MediaItemView)convertView).initialize((BOMedia) mediaItems.get(position));        
        return convertView;
    }

    public void setItems(ArrayList&lt;BOObject&gt; mediaItems) {
        this.mediaItems = mediaItems;
    }
}
</code></pre>

<p>MediaItemView.java</p>

<pre><code>public class MediaItemView extends LinearLayout {
    private static final String TAG = MediaItemView.class.getSimpleName();
    private BOMedia item;
    private SoftReference&lt;Bitmap&gt; bm;
    private ImageView iv;
    private Context ctx;

    public MediaItemView(Context context) {
        super(context);
        LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        layoutInflater.inflate(R.layout.view_media_item, this);
        this.ctx = context;
    }

    /** Init the view with a new BOMedia object
     * @param mediaItem
     */
    public void initialize(BOMedia mediaItem) {
        this.item = mediaItem;
        initUI();
    }

    private void initUI() {
        TextView title = (TextView) findViewById(R.id.itemText);
        iv = (ImageView) findViewById(R.id.itemImage);

        title.setText(Html.fromHtml(item.getTitle()));
        iv.setImageBitmap(null);
        bm = null;
        System.gc();
        iv.invalidate();
        if (item.getFilepathThumb() != null &amp;&amp; !item.getFilepathThumb().equals("")) {

            ExpansionPackManager.getInstance().getBitmapResource(item.getFilepathThumb(), false,
                    new BitmapReadListener() {

                        @Override
                        public void onFileRead(BitmapResponseMessage message) {
                            Log.d(TAG, "Bitmap read: " + message.getFilepath());
                            Bitmap image = message.getBitmap();
                            if (image != null &amp;&amp; message.getFilepath().equals(item.getFilepathThumb())) {
                                bm = new SoftReference&lt;Bitmap&gt;(image);
                                iv.setImageBitmap(bm.get());
                                Log.d(TAG, "image set");
                            } else {
                                Log.d(TAG, "image too late: " + image);
                            }
                        }

                        @Override
                        public void onFileException(Throwable exception) {
                            Log.d(TAG, "image exception");
                        }
                    });

        }
    }

}
</code></pre>
