web-dev-qa-db-fra.com

Android: exception de mémoire insuffisante dans Galerie

Mon application affiche une liste de 9 catégories et chaque catégorie affiche un flux de couverture basé sur Galerie (gracieusement offert par Neil Davies ici ) avec des images de la catégorie sélectionnée.
Les images sont extraites du Web, chacune d’une taille allant de 300 à 500 Ko, puis stockées dans une liste matricielle de tirables. Ces données sont liées au coverflow à l'aide d'un BaseAdapter (code ci-dessous).
Chaque fois que je quitte le coverflow et retourne à la liste des catégories, j'efface la liste de tableaux (encore une fois, le code ci-dessous).
Dans le scénario 1, mon tableauListe contient 5 Drawables. Dans ce scénario, je peux parcourir librement toutes les catégories et afficher leurs images. Au cours de mon test, j'ai parcouru toutes les catégories 5 fois, ce qui semble suffisant pour déterminer qu'il n'y a pas de problème.
Dans le scénario 2, mon tableauListe contient 10 objets dessinables. Dans ce scénario, je reçois une exception OutOfMemoryError lors de la lecture d'images dans la 5ème ou la 6ème catégorie: 

 07-13 08: 38: 21.266: ERREUR/dalvikvm-heap (2133): allocation externe de 819 840 octets trop importante pour ce processus. 
 07-13 08: 38: 21.266: ERROR/(2133): VM ne nous laissera pas allouer 819840 octets 
 07-13 08: 38: 21.277: DEBUG/skia (2133): --- decoder-> le décodage a renvoyé la valeur false 
 07-13 08: 38: 21.287: WARN/dalvikvm (2133): ID_fichier = 25: thread sortant avec une exception non capturée (groupe = 0x4001b188) 
 07-13 08: 38: 21.296: ERROR/AndroidRuntime (2133): Gestionnaire non capturé: thread Thread-64 qui se ferme en raison d'une exception non capturée 
 07-13 08: 38: 21.308: ERROR/AndroidRuntime (2133): Java.lang.OutOfMemoryError: la taille de la bitmap dépasse VM budget 
 07-13 08: 38: 21.308: ERREUR/AndroidRuntime (2133): sur Android.graphics.BitmapFactory.nativeDecodeStream (Méthode native) 
 07-13 08:38: 21.308: ERREUR/AndroidRuntime (2133): sur Android.graphics.BitmapFactory.decodeStream (BitmapFactory.Java:459) 
 07-13 08:38: 21.308: ERREUR/AndroidRuntime (2133): sur Android.graphics. BitmapFactory.decodeRe sourceStream (BitmapFactory.Java:323) 
 07-13 08: 38: 21.308: ERREUR/AndroidRuntime (2133): sur Android.graphics.drawable.Drawable.createFromResourceStream (Drawable.Java:697) 
 07-13 08: 38: 21.308: ERREUR/AndroidRuntime (2133): sur Android.graphics.drawable.Drawable.createFromStream (Drawable.Java:657) 

Cela n'a pas de sens pour moi. Si je perdais de la mémoire, je m'attendrais à ce que le scénario 1 se bloque à un moment donné, mais j'ai parcouru toutes les catégories un nombre considérable de fois et je ne me suis pas écrasé. J'ai également utilisé le plugin Memory Analyzer pour Eclipse qui ne présentait aucun coupable potentiel.
Si le système ne pouvait pas gérer 10 images, comme dans la scénario 2, je m'attendais à ce que la première catégorie tombe en panne, mais je ne tombe que 5 ou 6 catégories plus tard.
Quelques codes:

Les fonctions de l'adaptateur de 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;      
     }

remplir la source de données lors de l'entrée dans la catégorie: 

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);  

}

effacer la source de données lors de la sortie de la catégorie (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);  
            }    

}

MODIFIER:

D'accord, j'ai appliqué la fonction LogHeap de Mathias sur mon coverflow et voici quelques sorties. Avant de charger la première galerie:

 DEBUG/Application (5221): débogage. ================================. 
 DEBUG/Application (5221): debug.heap native: alloué 6,20 Mo de 6,28 Mo (0,07 Mo libre) dans [com.example.Coverflow] 
 DEBUG/Application (5221): debug.memory: alloué: 4,00 Mo de 24,00 Mo (0,00 Mo libre) 
 DEBUG/dalvikvm (5221): Le CPG a libéré 4558 objets/638152 octets en 84 ms 
 DEBUG/dalvikvm (5221): Le GC a libéré 17 objets/808 octets en 67 ms 
.

Après être entré dans la première galerie: 

 DEBUG/Application (5221): débogage. ================================. 
 DEBUG/Application (5221): debug.heap native: alloué 14,90 Mo de 16,89 Mo (0,07 Mo libre) dans [com.example.Coverflow] 
 DEBUG/Application (5221): debug.memory: alloué: 4,00 Mo de 24,00 Mo (1,00 Mo libre) 
 DEBUG/dalvikvm (5221): Le CPG a libéré 357 objets/50080 octets en 68ms 
 DEBUG/dalvikvm (5221): Le GC a libéré 353 objets/27312 octets en 67ms 
.

Après avoir existé la première galerie:

 DEBUG/Application (5221): débogage. ================================. 
 DEBUG/Application (5221): debug.heap native: alloué 14,83 Mo de 16,89 Mo (0,11 Mo libre) dans [com.example.Coverflow] 
 DEBUG/Application (5221): debug.memory: alloué: 4,00 Mo de 24,00 Mo (1,00 Mo libre) 
 DEBUG/dalvikvm (5221): GC libéré 330 objets/17920 octets en 77ms 
 DEBUG/dalvikvm (5221): GC libéra 13 objets/760 octets en 67ms 
.

Après être entré dans la cinquième galerie:

 DEBUG/Application (5221): débogage. ================================. 
 DEBUG/Application (5221): debug.heap native: alloué 16,80 Mo de 23,32 Mo (0,08 Mo libre) dans [com.example.Coverflow] 
 DEBUG/Application (5221): debug.memory: alloué: 4,00 Mo de 24,00 Mo (1,00 Mo libre) 
 DEBUG/dalvikvm (5221): Le CPG a libéré 842 objets/99256 octets en 73 ms 
 DEBUG/dalvikvm (5221): Le CPG a libéré 306 objets/24 896 octets en 69 ms 
.

Après avoir quitté la cinquième galerie:

 DEBUG/Application (5221): débogage. ================================. 
 DEBUG/Application (5221): debug.heap native: 16,74 Mo alloués de 23,32 Mo (0,11 Mo libre) dans [com.example.Coverlow] 
 DEBUG/Application (5221): debug.memory: alloué: 4,00 Mo de 24,00 Mo (1,00 Mo libre) 
 DEBUG/dalvikvm (5221): GC libère 331 objets/18184 octets en 68ms 
 DEBUG/dalvikvm (5221): GC libère 60 objets/3128 octets en 68ms 
.

Il semble que de plus en plus de mémoire est allouée lors de l'accès à une galerie, mais très peu est libérée après la sortie. Suis-je pas effacer mes drawables correctement? J'appelle setCallBack (null) pour chaque élément de ma liste de dessinables et je mets l'élément à null. N'est-ce pas assez?
Désespéré de toute idée.
Merci

20
Rob

Les images sont extraites du Web, D’une taille allant de 300K à 500K , puis stockées dans une liste de tableaux de .

La taille du fichier ko de l'image que vous chargez sur le Web n'est pas directement pertinente. Comme ils sont convertis en bitmaps, vous devez calculer width * height * 4 octets par image pour les images ARGB standard. (largeur et hauteur en px).

Les bitmaps consomment du tas natif, qui n'apparaît généralement pas dans un hprof. Hprof doit uniquement vous montrer le nombre d’objets, c’est-à-dire BitmapDrawables ou Bitmaps restants.

J'utilise ce code dans mon application pour afficher la mémoire actuellement utilisée utilisée par l'application et le tas natif:

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());
    }
}

que j'appelle lorsque je commence ou termine une activité en cours de développement.

logHeap(this.getClass());

Voici quelques liens informatifs - généralement, il y a beaucoup de discussions sur ce sujet ici.

Voici également une diapositive utile de Romain Guy (ingénieur Framework Android) sur les références logicielles, les références faibles, les caches simples, la gestion des images: http://docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI -HowtomakeyourAndroidUIfastandefficient.pdf

38
Mathias Conradt

Voici quelques conseils:

  1. Utilisez-vous l'option inSampleSize? Cela réduit la consommation de mémoire si vous redimensionnez les images. Étrange problème de mémoire insuffisante lors du chargement d'une image dans un objet Bitmap

  2. Vous devez appeler Bitmap.recycle () lorsque vous n'avez plus besoin d'images. Je pense que c'est important dans votre cas. Android: OutofMemoryError: la taille du bitmap dépasse VM budget sans raison, je peux voir

4
Fedor

Vous feriez mieux de savoir que la liste convertView dans la liste de paramètres getView est toujours null. C'est-à-dire que la galerie ne réutilise pas l'ancienne vue à l'intérieur.

0
user2046063

L'image que vous chargez dans la galerie 5 ou 6 est peut-être trop grande pour être chargée et dépasse la taille maximale autorisée par la machine virtuelle.

0
Ryan Conrad