Listview in Fragment está causando Memory Leak

Tengo una FragmentActivity con un FragmentMediaOverview contiene una lista de MediaItemView s (cada una con una vista de imagen y un poco de texto) y un clic en uno de los elementos de apertura de un fragmento de detalle. Ahora, cuando vuelvo (a través del botón de retroceso) y adelante (haga clic en listitem) varias veces de la lista a fragmento de detalle que finalmente se ejecutan en OOM-Errors. Utilizo SoftReference s para los mapas de bits en los listitems, así como en el fragmento de detalle. De acuerdo con MAT hay un número incresing de MediaItemView s, así como FragmentMediaOverview instancias, pero simplemente no puedo entender por qué.

He leído este Android: AlertDialog provoca una pérdida de memoria , pero no pudo solucionarlo anulando a los oyentes.

Aquí está mi código:

FragmentMediaOverview.java

(Esto no es un ListFragment porque para un tablet-layout el MediaAdapter necesita conectarse a un gridview)

 public class FragmentMediaOverview extends Fragment { private static String TAG = FragmentMediaOverview.class.getSimpleName(); private MediaAdapter adapter; private OnMediaSelectedListener selListener; private ArrayList<BOObject> 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<?> 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<BOObject> objects) { if (!getActivity().isFinishing()) { mediaItems = objects; Collections.sort(mediaItems, new Comparator<BOObject>() { final Collator c = Collator.getInstance(Locale.GERMAN); @Override public int compare(BOObject s1, BOObject s2) { if (s2 != null && ((BOMedia) s2).getTitle() != null && s1 != null && ((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); } } }); } } 

MediaAdapter.java

 public class MediaAdapter extends BaseAdapter { private static final String TAG = MediaAdapter.class.getSimpleName(); private Context context; private ArrayList<BOObject> mediaItems; public MediaAdapter(Context c, ArrayList<BOObject> 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<BOObject> mediaItems) { this.mediaItems = mediaItems; } } 

MediaItemView.java

 public class MediaItemView extends LinearLayout { private static final String TAG = MediaItemView.class.getSimpleName(); private BOMedia item; private SoftReference<Bitmap> 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 && !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 && message.getFilepath().equals(item.getFilepathThumb())) { bm = new SoftReference<Bitmap>(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"); } }); } } } 

En MediaItemView el tamaño de su mapa de bits debe ser demasiado grande. Si el mapa de bits es 600×600 y desea mostrar una imagen con un tamaño de 50×50 puede utilizar Bitmap.createScaledBitmap . También debe utilizar caché de mapa de bits al cargar su mapa de bits.

Esto se debe a que el View for rach child en ListView se vuelve a crear a medida que se desplaza. Esto es muy pesado en recursos. Para evitar esto use una clase de soporte en los adaptadores getView() para mantener y reutilizar las vistas. Esto se conoce como un Efficient Adapter . Por ejemplo, vea Efficient List Adapter en API demos . http://developer.android.com/tools/samples/index.html

También puedes usar:

 android:hardwareAccelerated = true 

A partir de Android 3.0 (API level 11) , la tubería de procesamiento de Android 2D está diseñada para soportar mejor la aceleración de hardware. La aceleración de hardware lleva a cabo todas las operaciones de dibujo que se realizan en el lienzo de View utilizando la GPU .

Para más información http://developer.android.com/guide/topics/graphics/hardware-accel.html

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.