RuntimeException: Canvas: trying to draw too large(120153600bytes) bitmap

Bonjour,

Je suis en train de faire les exercices diaporama et je rencontre un problème avec l’exécution du code sur certains mobile (NEXUS 6P par exemple), sur un oneplus 7 c’est bon.
D’après mes recherches, il s’agirait d’un problème avec la taille ou le format de l’image et la taille de l’écran, mais je ne trouve pas comment le résoudre et faire en sorte que le code fonctionne pour tous les appareils.
Une idée pour moi ?

La solution de Maxime fait pareille.

Merci par avance

2020-07-22 11:10:04.991 24663-24663/fr.redwolf.slideshow E/AndroidRuntime: FATAL EXCEPTION: main
Process: fr.redwolf.slideshow, PID: 24663
java.lang.RuntimeException: Canvas: trying to draw too large(120153600bytes) bitmap.
at android.view.DisplayListCanvas.throwIfCannotDraw(DisplayListCanvas.java:229)
at android.view.RecordingCanvas.drawBitmap(RecordingCanvas.java:97)
at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:529)
at android.widget.ImageView.onDraw(ImageView.java:1367)
at android.view.View.draw(View.java:19192)
at android.view.View.updateDisplayListIfDirty(View.java:18142)
at android.view.View.draw(View.java:18920)
at android.view.ViewGroup.drawChild(ViewGroup.java:4236)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4022)
at androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw(ConstraintLayout.java:2023)
at android.view.View.updateDisplayListIfDirty(View.java:18133)
at android.view.View.draw(View.java:18920)
at android.view.ViewGroup.drawChild(ViewGroup.java:4236)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4022)
at android.view.View.updateDisplayListIfDirty(View.java:18133)
at android.view.View.draw(View.java:18920)
at android.view.ViewGroup.drawChild(ViewGroup.java:4236)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4022)
at android.view.View.updateDisplayListIfDirty(View.java:18133)
at android.view.View.draw(View.java:18920)
at android.view.ViewGroup.drawChild(ViewGroup.java:4236)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4022)
at android.view.View.updateDisplayListIfDirty(View.java:18133)
at android.view.View.draw(View.java:18920)
at android.view.ViewGroup.drawChild(ViewGroup.java:4236)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4022)
at android.view.View.updateDisplayListIfDirty(View.java:18133)
at android.view.View.draw(View.java:18920)
at android.view.ViewGroup.drawChild(ViewGroup.java:4236)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4022)
at android.view.View.draw(View.java:19195)
at com.android.internal.policy.DecorView.draw(DecorView.java:788)
at android.view.View.updateDisplayListIfDirty(View.java:18142)
at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:669)
at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:675)
at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:783)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:2992)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2806)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2359)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1392)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6752)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:911)
at android.view.Choreographer.doCallbacks(Choreographer.java:723)
at android.view.Choreographer.doFrame(Choreographer.java:658)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

Salut @Wolf,
le message d’erreur indique aue ton image pèse 120 Mo c’est bien ça ?
Ca me paraît énorme pour une image, et il est possible que sur un téléphone avec peu de ram ça bloque

Hello, le problème c’est que j’utilise les images que tu nous as données pour l"exercice et ton code.
Il marche pour certains mobile, je sèche un peu

Hein ?

Je viens de jeter un regard sur la vidéo de l’exercice. Maxime ne donne pas d’images, il montre juste les images de chiens et de chats qu’il a utilisé, lui, expliquant que chaque développeur peut choisir des images de son coté.

Je ne sais pas d’où tu sort cette image de 120 Mo, mais c’est colossal. Celles de Maxime sont des .jpg ne devant pas prendre plus d’un Mo chacune (dommage que la vidéo ne montre pas la taille des images).

Le stockage bitmap des images, une fois chargées en mémoire par les applications, n’a rien a voir avec la compression des .jpg, .png et autres formats. C’est un stockage « pur et dur » où les pixels sont décompressés pour accélérer la vitesse de traitement graphique. Même une petite image .jpg peut être très volumineuse, une fois chargée en mémoire.

Un Canvas est un espace de travail bitmap, sa taille maximale est limité par le système pour ne pas consommer trop de mémoire. Cela peut varier en fonction de la version de l’OS et de la place mémoire disponible sur le Device.

Ex : si tu tentes d’afficher une image de 5.000x5.000 pixels sur un canvas limité 1024x1024 pixels, il vas forcément y avoir un problème …

Bonjour,

Merci pour l"explication sur le canevas qui est encore nébuleux pour moi.
Pour les images, j’utilise celles qui sont dans la solution proposée par Maxime, donc iso.
D’ailleurs j’ai utilisé la solution de Maxime sur un Nexus 6P (hardware) et un pixel XL (AVD) et j’ai exactement le même résultat.

Je suis au bout de mes tests.
Je galere aussi avec l’ajout de la ligne import qui ne se fait pas, mais ça c’est peut être pour un autre sujet ?

Logcat
Logcat_Pixel.zip (3,6 Ko)
Logcat_Nexus6P.zip (3,2 Ko)

Liste des images

Merci pour votre aide

Essaye d’afficher la taille de l’image, juste avant l’opération fatidique. (Non, je ne sais plus comment, cela fait au moins un an et demi que je n’ai pas touché à la programmation Android).

Hello,
Merci, je vais essayer de comprendre, parce que ça me bloque vraiment pour continuer le cours.

Tu peux aussi essayer de fabriquer des images de tests plus petites, en reprenant les fichiers de Maxime et en utilisant un utilitaire graphique pour réduire la hauteur et la largeur de 50%.

Tu bosses sur quel OS ? MacOS, Linux ou Windows ?

Windows.
Oui, je peux aussi, mais je n’aime pas ne pas savoir.:smiley:

Je vais finir par faire ça, mais je rage.

Attention, je ne te suggère pas de faire une manoeuvre magique pour régler le problème sur une configuration de device et pas une autre.

Pour le moment, la taille des images est juste une hypothèse, surtout que tu ne m’a toujours pas donné la taille réelle de tes graphismes. Le poids (en Ko) c’est une chose, la taille (largeur/hauteur) c’est autre chose.

Il faut donc :
a) vérifier l’hypothèse en modifiant la taille des images.
b) si c’est bien ça, essayez de déterminer le seuil précis de déclenchement du problème. Les seuils critiques sont généralement des puissances de 2 (32, 64, 128, 256, 512, 1024, 2048, etc …).

Par exemple, à une époque sur iOS, les images ne pouvaient pas dépasser 1024x1024 pixels. Il y a aussi eu un gag où le plantage se produisant en faisant des opérations graphiques dans la mémoire sur des images dépassant la taille physique de l’écran (très gênant, car cela fonctionnait sur les devices de grande taille et plantait sur les petits modèles).

C’est pour ce genre de choses qu’il est utile d’afficher le maximum d’informations pour trouver les bugs (taille en pixels des images, occupation mémoire, taille de l’écran physique, etc…). He oui, la recherche des bugs est souvent une tâche ingrate … Surtout quand c’est une bug intermittente (qui ne se produit pas systématiquement !).

c) En trouvant les conditions de plantage, tu peux écrire du code pour éviter que cela ne se produit.

genre : Si (tailleImage >= Seuil) on n’affiche pas l’image, mais un message prévenant l’utilisateur que les graphismes sont trop grand.


Au fait, est-ce que cela fonctionne sur les simulateurs ? Ou sur des devices physiques ?

Il serait intéressant de faire des tests avec différents simulateurs (avec des tailles d’écran différentes) pour voir si cela a une influence sur l’apparition du problème.

Draken, on sent l’experience.
Tu avais raison, ce n’est pas le poids qui fait planter, mais bien la taille en pixel.
La compression n’a rien changé, mais après avoir réduit le nombre de pixel, ça marche à fond.
Voici la taille qui fait planter 1920x1277 (Chat1 et chien1).

J’ai essayer sur 2 devices hardware

  • Nexus 6P → Crash (les logs sont dans le précédent post)
  • Oneplus 7T → OK

1 AVD

  • Pixel Xl → Crash (les logs sont dans le précédent post)

Version android
Je viens de comprendre que ça marche parfaitement sous Android 10, mon nexus et le Pixel XL (AVD) sont en Android 8.
ça veut dire qu’un mécanisme système android a évolué. Par contre, quoi et comment :frowning:


Pour la condition de test, je n’ai absolument rien trouvé sur comment avoir la taille d’une ressource image.
D’après mes recherche, les images en Drawable sont augmentées ou compressées pour se mettre à la résolution de l’écran, c’est ce que je constate dans les logs. Mais aucune solution n’a fonctionné.

Je pense que je ne suis bloqué pour suivre les cours de Maxime, mais si jamais vous avez une explication, pour ma compréhenssion, ce serait top.
Merci pour votre aide, je n’aurais surement pas trouvé tout seul.

Une taille impaire … hum … il est arrivé dans le passé que des API graphiques plantent avec des images de dimensions impaires. Ajuste la taille de l’image à 1920x1278 avec un logiciel graphique (ou une autre valeur proche divisible par 2) et essaye à nouveau.

Ce genre de bugs est difficile a repérer, car la quasi-totalité des images ont des dimensions paires.

Un lien vers la documentation de Kotlin, pour connaître la taille en pixel d’une image :

https://developer.android.com/topic/performance/graphics/load-bitmap

Avec des conseils pour optimiser le chargement (in English).

Re,

Merci pour tes conseils et la doc.
Je vais m’y atteler demain, là c’est un peu tard pour mon cerveau.

Par contre la taille de 1920x1278 n’a pas marché.

encore une fois merci

A ce stade, tu peux considérer que l’exercice est fini pour toi, et passer à l’étape suivante.

Le problème vient manifestement d’un bug dans l’OS, puisque cela fonctionne avec certaines versions d’Android et d’autres non. Tu n’as pas les connaissances nécessaires à ce stade de ton apprentissage pour le régler. C’est à Maxime de voir ce qui ne vas pas dans son exercice, et d’expliquer comment y remédier.

Hello @Draken

Je te confirme que je suis un peu jeune :smiley: en kotlin :stuck_out_tongue: pour implémenter la page que tu m"as transmise.
Merci pour ton aide, je vais effectivement poursuivre le cours en douceur (ce qui n’est déjà pas facile)

Merci pour ton aide.
@mbritto je te rajoute à la conversation au cas où.

Ok je viens de relire votre discussion et de regarder les sources fournies avec le TP.
Le problème vient en effet de la quantité de mémoire nécessaire pour allouer l’espace des images.
Le problème est que la fonction setImageResource est simple à utiliser mais ne gère pas l’optimisation des images chargées. Si l’image est trop grande pour la vue, elle la charge quand même à taille maximale, ce qui peut poser problème pour certains téléphones Android qui ont besoin d’économiser la ram.
La solution la plus simple sans dégrader la qualité d’affichage serait de gérer correctement les multiples tailles d’images : pour le moment toutes les images sont dans le dossier res/drawable.
Si tu as vu le chapitre sur la gestion des ressources, nous avions abordé la possibilité de séparer les résolutions pour une même image.
Il faudrait créer des version hdpi, xhdpi, xxhdpi de ces image : les tailles actuelles seraient les xxhdpi. Ainsi les téléphone ayant une faible densité iraient direct vers les versions plus petites des images (les téléphones ayant une plus grande densité ont généralement la ram nécessaire pour charger des images plus grosses)

D’après les tests de @Wolf, cela fonctionne sous Android 10.

Oui mais est-ce que ce sont les mêmes appareils (RAM, résolution d’écran, etc.) ?
Peut être aussi que sur Android 10 ils ont augmenté la limite d’allocation max et que du coup les images ne sont pas assez grosses pour un crash mais le problème de gaspillage de ram est bien présent

C’est ce que j’ai compris d’aprés les explications de @Wolf, les mêmes devices.

Oui, j’y ai pensé

Clairement, c’est énorme 120 Go. Cela correspond à une image 32 bits de 5300x5300 pixels.