it-swarm.com.de

Android: Keine Speicherausnahme in der Galerie

Meine App zeigt eine Liste von 9 Kategorien, und jede Kategorie zeigt einen Galerie-basierten Coverflow (freundlicherweise von Neil Davies hier ) mit Bildern der ausgewählten Kategorie an.
Die Bilder werden aus dem Web mit einer Größe von 300 bis 500 KB abgerufen und in einer ArrayList von Drawables gespeichert. Diese Daten sind mit einem BaseAdapter (Code unten) an den Coverflow gebunden.
Jedes Mal, wenn ich den Coverflow verlasse und zur Liste der Kategorien zurückkehre, lösche ich die ArrayList (wieder Code unten).
In Szenario 1 enthält meine ArrayList 5 Drawables. In diesem Szenario kann ich alle Kategorien frei durchsuchen und deren Bilder anzeigen. Während meines Tests habe ich fünfmal alle Kategorien durchlaufen, was genug scheint, um festzustellen, dass es kein Problem gibt.
In Szenario 2 enthält meine ArrayList 10 Drawables. In diesem Szenario erhalte ich eine OutOfMemoryError-Ausnahme, während ich Bilder innerhalb der 5. oder 6. Kategorie durchsehe: 

 07-13 08: 38: 21.266: FEHLER/Dalvikvm-Heap (2133): 819840 Byte Zuweisung für diesen Prozess zu groß. 
 07-13 08: 38: 21.266: ERROR/(2133): VM lässt uns nicht zu, dass 819840 Bytes 
 07-13 08: 38: 21.277: DEBUG/skia (2133): --- decoder-> decode zurückgegeben false 
 07-13 08: 38: 21.287: WARN/dalvikvm (2133): threadid = 25: Thread wird mit einer nicht erfassten Ausnahme beendet (group = 0x4001b188) 
 07-13 08: 38: 21.296: ERROR/AndroidRuntime ( 2133): Nicht erfasster Handler: Thread Thread-64 wurde aufgrund einer nicht erfassten Ausnahme beendet 
 07-13 08: 38: 21.308: ERROR/AndroidRuntime (2133): Java.lang.OutOfMemoryError: Bitmap-Größe überschreitet VM budget 
 07-13 08: 38: 21.308: FEHLER/Android-Laufzeit (2133): unter Android.graphics.BitmapFactory.nativeDecodeStream (native Methode) 
 07-13 08: 38: 21.308: ERROR/AndroidRuntime (2133): bei Android.graphics.BitmapFactory.decodeStream (BitmapFactory.Java:459) 
 07-13 08: 38: 21.308: ERROR/AndroidRuntime (2133): bei Android.graphics.BitmapFactory.decodeResour ceStream (BitmapFactory.Java:323) 
 07-13 08: 38: 21.308: ERROR/AndroidRuntime (2133): unter Android.graphics.drawable.Drawable.createFromResourceStream (Drawable.Java:697) 
 07-13 08: 38: 21.308: ERROR/AndroidRuntime (2133): unter Android.graphics.drawable.Drawable.createFromStream (Drawable.Java:657) 

Das macht für mich keinen Sinn. Wenn ich Gedächtnis leide, hätte ich erwartet, dass ich irgendwann in Szenario 1 abstürzen würde, aber ich habe alle Kategorien mehrmals durchlaufen und bin nicht abgestürzt. Ich habe auch das Memory Analyzer-Plugin für Eclipse verwendet, das keine potenziellen Täter darstellte.
Wenn das System 10 Bilder nicht verarbeiten kann, wie in Szenario 2, hätte ich mit einem Absturz in der ersten Kategorie gerechnet, aber ich stürze erst nach 5 oder 6 Kategorien ab.
Einige Codes:

Die Adapterfunktionen des Coverflow: 

public int getCount() {
     return DataManager.getInstance().getImageBufferInstance().getImageArraySize(); 
}

public Object getItem(int position) {    
     return DataManager.getInstance().getImagesBuffer().get(position);
}

public long getItemId(int position) {
     return position;
}

public View getView(int position, View convertView, ViewGroup parent) {      
         ImageView i;
         if (convertView == null)
             i = new ImageView(mContext);
         else
             i = (ImageView)convertView;
         Drawable bufferedImage = (Drawable)getItem(position);
         Log.v("getView", "position: " + position);
         i.setImageDrawable(bufferedImage);

         i.setLayoutParams(new CoverFlow.LayoutParams(Utils.getInstance().getScreenWidth() / 2,
                 Utils.getInstance().getScreenHeight() / 2));
         i.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 

         try{
         //Make sure we set anti-aliasing otherwise we get jaggies
         BitmapDrawable drawable = (BitmapDrawable) i.getDrawable();
         drawable.setAntiAlias(true);
         }
         catch (Exception e)
         {
             Log.v("getView", "Exception: " + e.toString());
         }
         return i;      
     }

füllen der Datenquelle bei Eintritt in die Kategorie: 

for (int i = 0; i < ImageBuffer.getInstance().getImageArraySize(); i++)  
{  
  String imageUrl = ImageBuffer.getInstance().getImageUrl(i);  
  Log.v("Initial", imageUrl);  
  Drawable fullImage = AsyncImageLoader.getInstance().loadImageByUrl(imageUrl);  
  ImageBuffer.getInstance().getImages().add(i, fullImage);  

}

löschen der Datenquelle beim Verlassen der Kategorie (in finish ()): 

for (int i = 0; i < ImageBuffer.getInstance().getImageArraySize(); i++)  
{  
  if (ImageBuffer.getInstance().images.get(i) != null)  
            {  
                ImageBuffer.getInstance().images.get(i).setCallback(null);  
                ImageBuffer.getInstance().images.set(i, null);  
            }    

}

BEARBEITEN:

OK, ich habe die LogHeap-Funktion von Mathias auf meinen Coverflow angewendet und hier einige Ausgaben. Vor dem Laden der ersten Galerie:

 DEBUG/Anwendung (5221): Debuggen. ================================== 
 DEBUG/Anwendung (5221): debug.heap native: 6,20 MB von 6,28 MB (0,07 MB frei) in [com.example.Coverflow] 
 DEBUG/Anwendung (5221) zugewiesen: debug.memory: zugewiesen: 4,00 MB von 24,00 MB (0,00 MB frei) 
 DEBUG/Dalvikvm (5221): GC befreite 4558 Objekte/638152 Bytes in 84 ms 
 DEBUG/Dalvikvm (5221): GC befreite 17 Objekte/808 Bytes in 67 ms 

Nach dem Betreten der ersten Galerie: 

 DEBUG/Anwendung (5221): Debuggen. ================================== 
 DEBUG/Anwendung (5221): debug.heap native: 14,90 MB von 16,89 MB (0,07 MB frei) in [com.example.Coverflow] 
 DEBUG/Anwendung (5221) zugewiesen: debug.memory: zugewiesen: 4,00 MB von 24,00 MB (1,00 MB frei) 
 DEBUG/Dalvikvm (5221): GC hat 357 Objekte/50080 Bytes in 68 ms 
 DEBUG/Dalvikvm (5221) freigegeben: GC hat 353 Objekte/27312 Bytes in 67 ms 

Nach dem Bestehen der ersten Galerie:

 DEBUG/Anwendung (5221): Debuggen. ================================== 
 DEBUG/Anwendung (5221): debug.heap native: 14.83 MB von 16.89 MB (0.11 MB frei) in [com.example.Coverflow] 
 DEBUG/Anwendung (5221): debug.memory: zugewiesen: 4.00 MB von 24.00 MB (1.00 MB frei) 
 DEBUG/dalvikvm (5221): GC befreite 330 Objekte/17920 Bytes in 77 ms 
 DEBUG/dalvikvm (5221): GC befreite 13 Objekte/760 Bytes in 67 ms 

Nach dem Betreten der fünften Galerie:

 DEBUG/Anwendung (5221): Debuggen. ================================== 
 DEBUG/Anwendung (5221): debug.heap native: 16,80 MB von 23,32 MB (0,08 MB frei) in [com.example.Coverflow] 
 DEBUG/Anwendung (5221): debug.memory: zugewiesen: 4,00 MB von 24,00 MB (1,00 MB kostenlos) 
 DEBUG/Dalvikvm (5221): GC befreite 842 Objekte/99256 Bytes in 73 ms 
 DEBUG/Dalvikvm (5221): GC befreite 306 Objekte/24896 Bytes in 69 ms 

Nach dem Verlassen der fünften Galerie:

 DEBUG/Anwendung (5221): Debuggen. ================================== 
 DEBUG/Anwendung (5221): debug.heap native: 16.74 MB von 23.32 MB (0,11 MB frei) in [com.example.Coverlow] 
 DEBUG/Anwendung (5221): debug.memory: zugewiesen: 4,00 MB von 24,00 MB (1,00 MB kostenlos) 
 DEBUG/Dalvikvm (5221): GC befreite 331 Objekte/18184 Bytes in 68 ms 
 DEBUG/Dalvikvm (5221): GC befreite 60 Objekte/3128 Bytes in 68 ms 

Es scheint, dass beim Betreten einer Galerie immer mehr Speicherplatz zugewiesen wird, nach dem Beenden jedoch nur sehr wenig freigegeben wird. Lösche ich meine Drawables nicht richtig? Für jedes Element in meiner ArrayList von Drawables rufe ich setCallBack (null) auf und setze das Element auf null. Ist das nicht genug?
Verzweifelt nach Einsicht.
Vielen Dank

20
Rob

Die Bilder werden aus dem Internet abgerufen, , Wobei die Größe Zwischen 300.000 und 500.000 liegt und in einer ArrayList von Drawables gespeichert wird.

Die kb-Dateigröße des Bildes, das Sie aus dem Internet laden, ist nicht direkt relevant. Da sie in Bitmaps umgewandelt werden, müssen Sie die Breite * Höhe * 4 Byte pro Bild für normale ARGB-Bilder berechnen. (Breite und Höhe in px).

Die Bitmaps verbrauchen nativen Heap, der normalerweise nicht in einem hprof angezeigt wird. Hprof sollte nur die Anzahl der Objekte anzeigen, d. H. BitmapDrawables oder Bitmaps, die übrig sind.

Ich verwende diesen Code in meiner App, um den aktuell von der App verwendeten Speicher und den systemeigenen Heap auszugeben:

public static void logHeap(Class clazz) {
    Double allocated = new Double(Debug.getNativeHeapAllocatedSize())/new Double((1048576));
    Double available = new Double(Debug.getNativeHeapSize())/1048576.0);
    Double free = new Double(Debug.getNativeHeapFreeSize())/1048576.0);
    DecimalFormat df = new DecimalFormat();
    df.setMaximumFractionDigits(2);
    df.setMinimumFractionDigits(2);

    Log.d(APP, "debug. =================================");
    Log.d(APP, "debug.heap native: allocated " + df.format(allocated) + "MB of " + df.format(available) + "MB (" + df.format(free) + "MB free) in [" + clazz.getName().replaceAll("com.myapp.Android.","") + "]");
    Log.d(APP, "debug.memory: allocated: " + df.format(new Double(Runtime.getRuntime().totalMemory()/1048576)) + "MB of " + df.format(new Double(Runtime.getRuntime().maxMemory()/1048576))+ "MB (" + df.format(new Double(Runtime.getRuntime().freeMemory()/1048576)) +"MB free)");
    System.gc();
    System.gc();

    // don't need to add the following lines, it's just an app specific handling in my app        
    if (allocated>=(new Double(Runtime.getRuntime().maxMemory())/new Double((1048576))-MEMORY_BUFFER_LIMIT_FOR_RESTART)) {
        Android.os.Process.killProcess(Android.os.Process.myPid());
    }
}

die ich beim Starten oder Beenden einer Aktivität während der Entwicklung anrufe.

logHeap(this.getClass());

Hier sind einige informative Links - im Allgemeinen gibt es viele Threads zu diesem Thema.

Hier ist auch eine nützliche Folie von Romain Guy (Android Framework Engineer) über weiche Referenzen, schwache Referenzen, einfache Caches und Bildverarbeitung: http://docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI -Wie können Sie IhreAndroidUIfastandefficient.pdf

38
Mathias Conradt

Hier einige Ratschläge:

  1. Verwenden Sie die Option inSampleSize? Dadurch wird der Speicherverbrauch reduziert, wenn Sie Bilder skalieren. Seltener Speicherfehler beim Laden eines Bildes in ein Bitmap-Objekt

  2. Sie sollten Bitmap.recycle () aufrufen, wenn Sie keine Bilder mehr benötigen. Ich finde es wichtig in deinem Fall. Android: OutofMemoryError: Die Bitmap-Größe überschreitet das Budget von VM, ohne dass ich einen Grund dafür sehe

4
Fedor

Sie sollten sich bewusst sein, dass die convertView in der Parameterliste von getView immer null ist. Das heißt, Galerie verwendet die alte Ansicht nicht wieder.

0
user2046063

Das Bild, das Sie in Galerie 5 oder 6 laden, ist möglicherweise zu groß zum Laden und überschreitet die von der VM zulässige Maximalgröße.

0
Ryan Conrad