web-dev-qa-db-fra.com

Android: BitmapFactory.decodeStream () sans mémoire avec un fichier de 400 Ko avec 2 Mo de pile libre

Mon application rencontre une erreur de MOO à la ligne suivante de la source:

image = BitmapFactory.decodeStream(assetManager.open(imgFilename));

Juste avant l'allocation qui provoque la destruction de l'application avec une erreur de MOO:

(...)
08-05 21:22:12.443: I/dalvikvm-heap(2319): Clamp target GC heap from 25.056MB to 24.000MB
08-05 21:22:12.443: D/dalvikvm(2319): GC_FOR_MALLOC freed <1K, 50% free 2709K/5379K, external 18296K/19336K, paused 58ms
08-05 21:22:14.513: D/dalvikvm(2319): GC_EXTERNAL_ALLOC freed <1K, 50% free 2709K/5379K, external 18296K/19336K, paused 101ms
08-05 21:22:14.903: I/dalvikvm-heap(2319): Clamp target GC heap from 25.073MB to 24.000MB
08-05 21:22:14.903: D/dalvikvm(2319): GC_FOR_MALLOC freed 0K, 50% free 2709K/5379K, external 18312K/19336K, paused 53ms
08-05 21:22:22.843: D/ddm-heap(2319): Heap GC request
08-05 21:22:22.963: I/dalvikvm-heap(2319): Clamp target GC heap from 25.073MB to 24.000MB
08-05 21:22:22.963: D/dalvikvm(2319): threadid=1: still suspended after undo (sc=1 dc=1)
08-05 21:22:22.963: D/dalvikvm(2319): GC_EXPLICIT freed 1K, 50% free 2710K/5379K, external 18312K/19336K, paused 116ms

DDMS rapporte une image similaire à propos de l'état du tas:

Heap Size:  5.254 MB
Allocated:  2.647 MB
Free:   2.607 MB
%Used:  50.38%
#Objects    49,028  

Le fait de ne pas franchir cette ligne entraîne une erreur de MOO:

08-05 21:26:04.783: D/dalvikvm(2319): GC_EXTERNAL_ALLOC freed <1K, 50% free 2710K/5379K, external 18312K/19336K, paused 57ms
08-05 21:26:05.023: E/dalvikvm-heap(2319): 2097152-byte external allocation too large for this process.
08-05 21:26:05.163: I/dalvikvm-heap(2319): Clamp target GC heap from 25.073MB to 24.000MB
08-05 21:26:05.163: E/GraphicsJNI(2319): VM won't let us allocate 2097152 bytes
08-05 21:26:05.163: D/dalvikvm(2319): GC_FOR_MALLOC freed 0K, 50% free 2710K/5379K, external 18312K/19336K, paused 30ms
08-05 21:26:05.283: D/skia(2319): --- decoder->decode returned false
  1. La taille du fichier référencé par "imgFileName" est <400K sous Windows. Alors pourquoi BitmapFactory.decodeStream essaie-t-il d'allouer 2 Mo?
  2. Pourquoi y a-t-il une erreur de MOO alors qu'il semble y avoir suffisamment d'espace libre?

Cette application vise Android 2.2 et plus.

Merci d'avance!

65
Mayank

La bibliothèque Android n'est pas aussi intelligente pour charger des images, vous devez donc créer des solutions de contournement pour cela.

Dans mes tests, Drawable.createFromStream utilise plus de mémoire que BitmapFactory.decodeStream.

Vous pouvez modifier le schéma de couleurs pour réduire la mémoire (RGB_565), mais la qualité de l'image en perdra également:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);

Référence: http://developer.Android.com/reference/Android/graphics/Bitmap.Config.html

Vous pouvez également charger une image à l'échelle, ce qui réduira considérablement l'utilisation de la mémoire, mais vous devez connaître vos images pour ne pas en perdre trop en qualité.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);

Référence: http://developer.Android.com/reference/Android/graphics/BitmapFactory.Options.html

Pour définir le inSampleSize de manière dynamique, vous voudrez peut-être connaître la taille de l'image pour prendre votre décision:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
bitmap = BitmapFactory.decodeStream(stream, null, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;

options.inJustDecodeBounds = false;
// recreate the stream
// make some calculation to define inSampleSize
options.inSampleSize = ?;
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);

Vous pouvez personnaliser le inSampleSize en fonction de la taille de l'écran du périphérique. Pour obtenir la taille de l'écran, vous pouvez faire:

DisplayMetrics metrics = new DisplayMetrics();
((Activity) activity).getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenWidth = metrics.widthPixels;
int screenHeight =metrics.heightPixels;

Autres tutoriels: - http://developer.Android.com/training/displaying-bitmaps/load-bitmap.html - http://developer.Android.com/training/displaying -bitmaps/index.html

85
Paulo Cheque

Veuillez consulter ceci pour obtenir un guide sur le chargement plus efficace de grandes bitmaps:

http://developer.Android.com/training/displaying-bitmaps/load-bitmap.html

Un fichier image de 400 Ko peut facilement occuper 5 à 10 Mo de RAM.

42
Michell Bak

La taille du fichier sur le disque ne coïncide pas nécessairement avec la taille du fichier en mémoire. Il est probable que le fichier soit compressé, ce qui ne sera pas le cas lors du décodage. Vous devez en tenir compte dans votre calcul.

Multipliez la taille de l'image (largeur x hauteur) par la profondeur de couleur de l'image pour obtenir la taille en mémoire de l'image.

8
nEx.Software

En gros, vous pouvez résoudre votre problème en essayant de redimensionner votre Bitmap et vous constaterez une consommation de mémoire réduite. Pour le faire, vous pouvez copier la méthode montrée ici .

En outre, il existe une page dédiée à Android Développeurs qui pourrait vous aider à mieux comprendre comment charger des bitmaps volumineux. Consultez l’officiel documentation .

3
yugidroid

Bien que les réponses ci-dessus soient évidemment correctes, une meilleure pratique consiste également à définir explicitement la propriété bitmap/src du ImageView sur null, lorsqu'elles ne sont plus utilisées, généralement lorsque votre l'activité est en cours de destruction. Toute autre ressource lourde (texte volumineux, audio, vidéo), etc. peut également être annulée. Cela garantit que les ressources sont libérées instantanément et que vous n'attendez pas que le GC soit collecté.

1
rishabh

J'ai résolu le problème OutOfMemoryError en utilisant la méthode ci-dessous

private Bitmap downloadImageBitmap(String sUrl) {
        this.url = sUrl;
        bitmap = null;
                    try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            for (options.inSampleSize = 1; options.inSampleSize <= 32; options.inSampleSize++) {
                InputStream inputStream = new URL(sUrl).openStream();  
                try {
                    bitmap = BitmapFactory.decodeStream(inputStream, null, options);      
                    inputStream.close();
                    break;
                } catch (OutOfMemoryError outOfMemoryError) {
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return bitmap;
    }
1
varotariya vajsi