2015-03-05 2 views
0

J'ai une application qui obtient une quantité fixe d'images à partir de l'aperçu de la caméra et les convertit en une liste de Bitmaps. A cette fin, je le code suivant:OOM lors de la création de bitmaps sur une application Android

private Camera.PreviewCallback SetPreviewCallBack() { 
    return new Camera.PreviewCallback() { 
     @Override 
     public void onPreviewFrame(byte[] data, Camera camera) { 
    List<Bitmap> imageList = new ArrayList<Bitmap>(); 
    for (int i=0; i<30; i++) // Let's suppose this is the real loop. 
          // It's not, as the real loop takes each camera preview frame, 
          // instead of inserting the same one 30 times. 
          // But for this example, it's OK 
    { 
     imageList.add(GetBitmap(
        data, 
        previewWidth, // Calculated 
        previewHeight, // Calculated 
        previewFormat, // Calculated 
        previewRotation)); // Calculated 
    } 

} 

private Bitmap GetBitmap(byte[] data, int width, int height, int previewFormat, int rotation) { 

    YuvImage yuv = new YuvImage(data, previewFormat, width, height, null); 

    ByteArrayOutputStream out = new ByteArrayOutputStream(); 
    yuv.compressToJpeg(new Rect(0, 0, width, height), 50, out); 

    byte[] bytes = out.toByteArray(); 
    final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); 

    Bitmap imageResult = RotateImage(bitmap, 4 - rotation); 
    bitmap.recycle(); 

    return imageResult; 
} 

private Bitmap RotateImage(Bitmap rotateImage, int rotation) { 

    Matrix matrix = new Matrix(); 
    switch (rotation) { 
    case 1: 
     matrix.postRotate(270); 
     break; 
    case 2: 
     matrix.postRotate(180); 
     break; 
    case 3: 
     matrix.postRotate(90); 
     break; 
    } 

    return Bitmap.createBitmap(rotateImage, 0, 0, rotateImage.getWidth(), 
      rotateImage.getHeight(), matrix, true); 
} 

Ce que je fais est: - je stocker cette image sur une classe Singleton afin d'y accéder d'un autre Activity contanied dans ma même application. - J'appelle à nouveau ce morceau de code (lorsqu'un événement se produit) et répète le processus d'extraction/de sauvegarde de l'image.

03-05 09:35:13.339: E/AndroidRuntime(8762): FATAL EXCEPTION: Thread-818 
03-05 09:35:13.339: E/AndroidRuntime(8762): java.lang.OutOfMemoryError 
03-05 09:35:13.339: E/AndroidRuntime(8762):  at android.graphics.Bitmap.nativeCreate(Native Method) 
03-05 09:35:13.339: E/AndroidRuntime(8762):  at android.graphics.Bitmap.createBitmap(Bitmap.java:726) 
03-05 09:35:13.339: E/AndroidRuntime(8762):  at android.graphics.Bitmap.createBitmap(Bitmap.java:703) 
03-05 09:35:13.339: E/AndroidRuntime(8762):  at android.graphics.Bitmap.createBitmap(Bitmap.java:636) 
03-05 09:35:13.339: E/AndroidRuntime(8762):  at com.facephi.sdk.ui.CameraPreview.RotateImage(CameraPreview.java:779) 
03-05 09:35:13.339: E/AndroidRuntime(8762):  at com.facephi.sdk.ui.CameraPreview.GetBitmap(CameraPreview.java:712) 

J'essaie d'utiliser les meilleures pratiques pour éliminer de la manière correcte Bitmaps utilisé comme posté here, mais pas de chance ...

Toute idée pourquoi je vais avoir cette erreur OOM?

+0

Définissez 'android: largeHeap =" true "' pour l'activité et également 'BitmapFactory.options' pour mettre à l'échelle vos objets bitmap avec la propriété' inSampleSize'. – Piyush

+0

@Devill en utilisant 'android: largeHeap' résout mon problème, mais j'ai bien peur qu'il finisse par planter car cela ne fait qu'augmenter la quantité de mémoire que l'application aura, mais à un moment je l'atteindrai aussi. Et qu'est-ce que 'BitmapFactory.options 'faire? – Sonhja

+0

OOM signifie qu'il dépasse la limite de mémoire de la taille du tas. Pour 'BitmapFactory.Options' vous pouvez vérifier http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html – Piyush

Répondre

1

1. Essayez d'utiliser

BitmapFactory.Options options=new BitmapFactory.Options(); 
options.inSampleSize = 8; 
options.inDither = false; 
options.inPurgeable = true; 
options.inInputShareable = true; 
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); 

Cela va grandement aider à économiser de la mémoire.

2. Ne pas "stocker" votre objet Bitmap dans un singleton ou n'importe où ailleurs. Cela conduira toujours à OutOfMemoryError. Créez le Bitmap à chaque fois que vous voulez l'utiliser.

EDIT:

Voir aussi le ThreadSample officiel exemple et le tutoriel Displaying Bitmaps Efficiently.

+0

Quel est le paramètre 'is'? – Sonhja

+0

Je stocke cette liste d'images sur un 'Singleton' parce que j'ai besoin de passer cette liste d'images entre deux activités. Si j'essaie de l'analyser, je dépasse toujours la limite "Parcelable". Mais laissez-moi essayer votre solution. Si je suis assez chanceux, l'utilisation de ce 'BitmapFactory.Options' permettra de réduire la taille de l'image afin que je puisse directement l'analyser et je n'ai pas besoin de le stocker dans une classe' Singleton'. – Sonhja

+0

J'ai édité la réponse que vous utilisez 'decodeByteArray()' au lieu de 'decodeStream()' –

2

Vous avez une mémoire très limitée sous Android, c'est pourquoi vous bénéficiez de cette exception. En général, vous ne devriez pas avoir besoin de stocker autant d'images en mémoire et ne devez les charger que lorsque vous en avez besoin (c'est-à-dire les montrer ou faire quelque chose avec elles) et les éliminer (recycle()) dès que vous avez terminé leur. De plus, vous ne devez les charger qu'avec une résolution aussi basse que possible. Après avoir dit tout cela, et ne sachant pas pourquoi vous devriez les avoir tous en mémoire (vous pouvez avoir une raison légitime), vous pouvez augmenter la taille de tas pour votre application en spécifiant l'attribut approprié dans votre manifeste (http://developer.android.com/guide/topics/manifest/application-element.html#largeHeap) mais même cela ne garantit pas que vous pouvez charger trop d'images. N'oubliez pas que par défaut, Android utilisera 4 octets par pixel pour vos images, donc si vous avez 30 images, chacune avec une densité de 1 million de pixels, vous utilisez 120 millions d'octets ou environ 120 Mo de mémoire. En revanche, l'octet alloué par défaut à votre application peut être aussi faible qu'un maigre 16mb (dépend de nombreux cas.Voir ici pour plus de détails: Android heap size on different phones/devices and OS versions).

Si vous pouvez modifier votre code de façon à ce que vous n'ayez pas à les charger en mémoire, vous pouvez consulter la mise en cache bitmap (http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html) qui peut grandement vous aider à résoudre vos problèmes de gestion de la mémoire.

+1

Il est à noter que vous pouvez réduire la quantité d'octets alloués par pixel en allouant les bitmaps avec Config.RGB_565 (ces bitmaps n'auront pas un canal alpha de couleur légèrement inférieure, mais si vous n'en avez pas besoin, cela réduira votre allocation de mémoire de moitié). –

+0

@kha Intéressant ... laissez-moi essayer. Bien sûr, comme vous l'avez mentionné, j'ai une raison légitime de savoir pourquoi j'ai besoin de ces images. J'insiste pour ne pas les stocker, mais jusqu'à présent je dois les avoir. Je vais vérifier tes liens. Ils ont l'air intéressants. – Sonhja

+0

@GilMoshayof Très juste point. Je faisais juste référence au comportement par défaut. Je le change moi-même en RGB_565 mais la plupart des gens vont simplement utiliser la valeur par défaut, c'est pourquoi j'ai écrit cela comme exemple. L'exemple dans la base de code dans la question initiale ne le changeait pas. – kha