2012-06-27 15 views
5

Ho un FragmentActivity con un FragmentMediaOverview contenente un elenco di MediaItemView s (ciascuno con una visualizzazione di immagine e del testo) e un clic su uno degli elementi che aprono un dettaglio-Frammento. Ora quando torno indietro (tramite pulsante indietro) e avanti (fai clic su listitem) più volte dall'elenco al frammento di dettaglio, eseguo eventualmente OOM-Errors. Io uso SoftReference s per le bitmap nei listini e nel frammento di dettaglio. Secondo MAT esiste un numero crescente di MediaItemView s come pure le istanze FragmentMediaOverview, ma non riesco a capire perché.Listview in Fragment sta causando perdite di memoria

Ho letto questo Android: AlertDialog causes a memory leak, ma non ho potuto risolverlo. null gli ascoltatori.

Ecco il mio codice:

FragmentMediaOverview.java

(Questo non è un ListFragment perché per un tablet-layout di MediaAdapter le esigenze di connettersi a una 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"); 
         } 
        }); 

     } 
    } 

} 
+0

Idee: 'ExpansionPackManager' potrebbe essere in possesso di riferimenti a bitaps oppure' BitmapResponseMessage's se sono mantenuti in un pool di riciclo come Android 'Message'. Per quest'ultimo, un altro codice dovrebbe chiamare 'message.recycle()'. – Gene

+0

avere uno sguardo su di esso .. http://stackoverflow.com/questions/9933783/double-checking-if-fragment-view-holder-pattern-is-implemented-properly E io preferisco di guardare il [presentazione Google IO ] (http://www.google.com/events/io/2010/sessions/world-of-listview-android.html) su 'ListViews'. Esso contiene tecniche di risparmio di memoria e mi ha aiutato a risolvere e capire la logica del 'ListViews'. E anche vedere questo [File PDF.] (Http://dl.google.com/googleio/2010/android-world-of-listview-android.pdf) –

risposta

1

Questo perché il View per il bambino Rach nel ListView è ricreato come si scorre. Questo è molto pesante sulle risorse. Per evitare ciò, utilizzare una classe di supporto negli adattatori getView() per conservare e riutilizzare le viste. Questo è chiamato Efficient Adapter. Ad esempio, vedere Efficient List Adapter in API demos. http://developer.android.com/tools/samples/index.html

+0

Grazie per il suggerimento. Questo potrebbe renderlo un po 'più efficiente, ma non spiegherebbe una perdita di memoria vero ?! Mentre sto riutilizzando il ConvertView, non ci dovrebbero essere quelli morti che fluttuano intorno ... Ho la sensazione che in qualche modo le voci della lista si stiano riprendendo da un curriculum/stampa retrospettiva, ma quelle vecchie sono conservate ... da qualche parte ... – Till

0

È anche possibile utilizzare:

android:hardwareAccelerated = true 

A partire dal Android 3.0 (API level 11), il Android 2D pipeline di rendering è stato progettato per una migliore accelerazione hardware di supporto. L'accelerazione hardware esegue tutte le operazioni di disegno eseguite sulla tela di una vista utilizzando GPU.

Per maggiori informazioni http://developer.android.com/guide/topics/graphics/hardware-accel.html

2

Nel MediaItemView la dimensione della vostra bitmap deve essere troppo grande. Se la bitmap è 600 x 600 e si desidera visualizzare un'immagine con una dimensione di 50 x 50, è possibile utilizzare Bitmap.createScaledBitmap. Dovresti anche usare la cache bitmap durante il caricamento della tua bitmap.

Problemi correlati