IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Android Discussion :

Problème de mémoire avec des Bitmap


Sujet :

Android

  1. #1
    Membre extrêmement actif
    Avatar de Ryu2000
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2008
    Messages
    9 552
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2008
    Messages : 9 552
    Points : 18 446
    Points
    18 446
    Par défaut Problème de mémoire avec des Bitmap
    Bonjour,
    Je réalise une application dans laquelle il y a une GridView, en WXGA il y a 36 vignettes qui s'affichent, dans chaque vignette il y a un bitmap.

    En fait les images sont stocké dans une base de données en Blob.
    L'application les télécharge et les stock dans la base de donnée SQLite en Blob.
    Ensuite l'application récupère un byte[] qui est transformé en Bitmap, comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    public Bitmap byteToBitmap(byte[] image){
         BitmapFactory.Options opt = new BitmapFactory.Options();
         opt.inSampleSize = 8;
         opt.inTempStorage = new byte[16 * 1024];
         Bitmap bm = BitmapFactory.decodeByteArray(image, 0, image.length, opt);
         return bm;
    }
    Là j'ai mis un SampleSize de 8, du coup les images sont très petites.

    En fait le téléchargement à lieu dans l'Adapter, j'ai créer une classe qu'extends CursorAdapter et qui implémente :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public View getView(int position, View convertView, ViewGroup parent)
    Je n'utilise pas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public void bindView(View view, Context context, Cursor cursor)
    ni :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public View newView(Context context, Cursor cursor, ViewGroup parent)
    Je n'ai pas compris comment elles fonctionnaient, j'ai l'impression qu'il faut mettre 2 fois le même code quasiment.

    Donc dans cet Adapter je regarde un HashMap <String, boolean> la clé en String est l'adresse de l'image et le boolean c'est pour dire si elle est en train d'être téléchargé ou si c'est la première fois qu'on affiche la vignette.

    Et si l'image n'a pas encore été téléchargé je créer une instance de classe qui extends AsyncTask <String, Void, Bitmap>, c'est plutôt sympa car en PostExecute ça setImageBitmap sur une ImageView et les vignettes apparaissent petit à petit.

    Par contre ça créer 36 threads en même temps la première fois

    Et du coup ça peut provoquer :
    java.lang.OutOfMemoryError

    Comment dois-je gérer la mémoire ?
    Est-ce que c'est une mauvaise idée de faire une AsyncTask par vignette ou est-ce que je devrais tout télécharger dans un seul thread ?

    Si vous avez des conseils à me donner, merci de m'en faire part.

    Ici on parle de téléchargé des images :
    http://android-developers.blogspot.f...rformance.html

    Ça fait quelque chose comme ça :
    http://code.google.com/p/android-ima...ownloader.java
    Keith Flint 1969 - 2019

  2. #2
    Modérateur
    Avatar de Hizin
    Homme Profil pro
    Développeur mobile
    Inscrit en
    Février 2010
    Messages
    2 180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

    Informations professionnelles :
    Activité : Développeur mobile

    Informations forums :
    Inscription : Février 2010
    Messages : 2 180
    Points : 5 072
    Points
    5 072
    Par défaut
    De mon côté, je ferai comme toi, à ceci près que je ferai un manager de DL en prime. Il lance 4 téléchargements (chiffre arbitraire) et, dès que l'un finit et qu'il reste des DL, lancement du/des suivant(s).
    Ca perd en temps de téléchargement (quoi que ... 9 fois moins d'utilisation du réseau, est-ce que les téléchargements n'iront pas plus vite ?), mais ça devrait y gagner en mémoire.
    C'est Android, PAS Androïd, ou Androïde didiou !
    Le premier est un OS, le second est la mauvaise orthographe du troisième, un mot français désignant un robot à forme humaine.

    Membre du comité contre la phrase "ça marche PAS" en titre et/ou explication de problème.

    N'oubliez pas de consulter les FAQ Android et les cours et tutoriels Android

  3. #3
    Membre extrêmement actif
    Avatar de Ryu2000
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2008
    Messages
    9 552
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2008
    Messages : 9 552
    Points : 18 446
    Points
    18 446
    Par défaut
    D'accord, je vais essayer de gérer les AsyncTask pour éviter d'en lancer trop en même temps.

    Ou peut être que je vais télécharger toutes les images dans un seul thread avant d'afficher la liste.
    Parce que parfois il y a plusieurs fois la même image et du coup toutes les ImageView ne sont pas à jour.

    Et sinon avec les Bitmap, comment ça fonctionne au niveau de recycle() ?
    Je dois appeler certaines méthode à un moment ?
    Keith Flint 1969 - 2019

  4. #4
    Modérateur
    Avatar de Hizin
    Homme Profil pro
    Développeur mobile
    Inscrit en
    Février 2010
    Messages
    2 180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

    Informations professionnelles :
    Activité : Développeur mobile

    Informations forums :
    Inscription : Février 2010
    Messages : 2 180
    Points : 5 072
    Points
    5 072
    Par défaut
    Le seul inconvénient du thread unique, c'est que tu va télécharger les images une par une, et donc, rallonger le temps d'attente de l'utilisateur.
    À voir si, dans ton cas, en Wifi et en 3G (si tu n'as pas de contrôle demandant spécifiquement de passer en Wifi pour les affichage) c'est tolérable ou non.

    Si tu as plusieurs fois la même image à afficher, peut-être tenter de les identifier ? Cela te fera ça de moins à télécharger et à créer/allouer.

    Au niveau du recycle, voici ce qu'en dit la doc :
    Citation Envoyé par [url=http://developer.android.com/reference/android/graphics/Bitmap.html#recycle%28%29]Doc[/url]
    Free the native object associated with this bitmap, and clear the reference to the pixel data. This will not free the pixel data synchronously; it simply allows it to be garbage collected if there are no other references. The bitmap is marked as "dead", meaning it will throw an exception if getPixels() or setPixels() is called, and will draw nothing. This operation cannot be reversed, so it should only be called if you are sure there are no further uses for the bitmap. This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.
    Il est donc recommandé de ne pas l'utiliser en temps normal. Personnellement, tant que je ne développe pas spécifiquement pour ICS et supérieur, je préfère l'utiliser (explication en fin).
    Cette méthode n'est a appeler qu'à un seul moment : quand tu es sûr que tu n'auras plus du tout besoin de l'image. Une fois libérée, elle est détruite (enfin ... comme écrit, elle est marquée pour le GC, donc pas libérée dans l'instant, mais tu n'auras plus besoin de t'en soucier).
    Si tu tentes de la ré-utiliser après, tu auras une exception (je ne sais plus laquelle) avec un message assez évocateur "Aren't you trying to use an image thas has been recycled ?".


    De mon côté, je préfère l'utiliser bien qu'il faille faire assez attention à son utilisation (l'exception en question arrive assez facilement si on ne fait pas gaffe ). Quand il y a pas mal de création/destruction d'images, je la pense même obligatoire.

    Tu peux, par exemple, vérifier la mémoire utilisée via Debug.getNativeHeapSize() doc), ainsi que plusieurs outils du DDMS (bon, ceux-là, j'ai plus de mal à les comprendre ... doc).


    Explication ICS :
    Je suis en train de reprendre une appli devant fonctionner à partir de la 2.3. Niveau conso mémoire, c'était un assez gros gouffre au niveau des images (images + textes créé par réflexions). Elle plantait assez vite sous le OutOfMemory après un peu d'utilisation et de visionnement.
    Sous GingerBread, elle consomme beaucoup. Sous ICS, elle consomme presque rien. À priori, ils ont beaucoup amélioré la gestion mémoire des images.
    À la création, sous GingerBread, j'ai 7.3 Mo de mémoire utilisée, sous ICS, j'ai 3.7 Mo.
    Après visionnement de la plupart des images (environs 25), je suis à 16.5 Mo sous Gingerbread et 4.2 Mo sous ICS.

    Remarque : le nombre d'image est toujours à multiplier par 2 : l'image + par réflexion.

    EDIT : à priori, c'est à partir de HoneyComb que la gestion mémoire des images a été améliorée plutôt (test sur tablette équivalent au test sur board ICS).
    C'est Android, PAS Androïd, ou Androïde didiou !
    Le premier est un OS, le second est la mauvaise orthographe du troisième, un mot français désignant un robot à forme humaine.

    Membre du comité contre la phrase "ça marche PAS" en titre et/ou explication de problème.

    N'oubliez pas de consulter les FAQ Android et les cours et tutoriels Android

  5. #5
    Membre extrêmement actif
    Avatar de Ryu2000
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2008
    Messages
    9 552
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2008
    Messages : 9 552
    Points : 18 446
    Points
    18 446
    Par défaut
    Pour l'instant mon application est pour Android 2.1, mais en fait elle devra être compatible pour Android 1.6...
    C'est un peu dommage :
    http://developer.android.com/resourc...-versions.html

    En fait l'application smartphone sera la même que l'application tablet, c'est dommage en 3.0 j'aurai peut être moins de problème de mémoire.

    J'essaie de gérer les téléchargements, mais je ne sais pas encore exactement comment faire.

    Je vais récupérer des couples :
    String : adresse de l'image à télécharge
    ImageView : l'ImageView dans laquelle afficher le Bitmap

    Après je ne sais pas vraiment comment faire pour lancer quelques téléchargements et en relancer quand il y en a qui finissent.
    Keith Flint 1969 - 2019

  6. #6
    Modérateur
    Avatar de Hizin
    Homme Profil pro
    Développeur mobile
    Inscrit en
    Février 2010
    Messages
    2 180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

    Informations professionnelles :
    Activité : Développeur mobile

    Informations forums :
    Inscription : Février 2010
    Messages : 2 180
    Points : 5 072
    Points
    5 072
    Par défaut
    Bon ... là, pour le coup, je ne peux pas t'apporter une autre aide, désolé.

    Toute les archis auxquels je pense et que je tente de mettre en oeuvre pour ce type de solution présente des défauts monstre x)
    À priori, ça fait un poil trop longtemps que je n'ai pas manipulé des Thread et des Handler.
    C'est Android, PAS Androïd, ou Androïde didiou !
    Le premier est un OS, le second est la mauvaise orthographe du troisième, un mot français désignant un robot à forme humaine.

    Membre du comité contre la phrase "ça marche PAS" en titre et/ou explication de problème.

    N'oubliez pas de consulter les FAQ Android et les cours et tutoriels Android

  7. #7
    Membre extrêmement actif
    Avatar de Ryu2000
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2008
    Messages
    9 552
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2008
    Messages : 9 552
    Points : 18 446
    Points
    18 446
    Par défaut
    Ok je regarderai ça plus tard.

    Pour l'instant je fais tout les téléchargements, la première fois que l'utilisateur se connecte et je n'ai plus du tout de problème de mémoire.
    Keith Flint 1969 - 2019

  8. #8
    Membre extrêmement actif
    Avatar de Ryu2000
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2008
    Messages
    9 552
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2008
    Messages : 9 552
    Points : 18 446
    Points
    18 446
    Par défaut
    Les images sont dans la base de données.
    Le problème c'est que transformer un tableau d'octet en bitmap ça prend un peu de temps, surtout sur tablettes quand il y a 30 images à afficher.

    Du coup j'ai une classe qu'extends AsyncTask, le constructeur reçoit une View, à partir de cette View je récupère une ImageView.

    Dans le doInBackground je récupère le tableau d'octet et le transforme en Bitmap.
    Dans le postExecute je met à jour l'ImageView.

    J'ai re implémenté bindView et newView, je trouvais que c'était plus fluide.
    Et dans bindView j’exécute l'AsyncTask en envoyant la View et la clé de l'image.

    La GridView s'affiche plus vite, mais parfois ça déconne.
    Des fois la mauvaise image s'affiche.

    Au début au lieu d'envoyer la View, j'envoyais directement l'ImageView, mais ça déconnait encore plus.

    Comment faire pour afficher une GridView et que les images apparaissent après, afin d’accélérer l'affichage ?

    ======================================================
    Edit :
    Au final j'ai reviré bindView et newView et j'utilise getView.
    Je ne comprend rien à bindView et newView.

    Donc là j'ai fais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Cursor cursor = this.getCursor();
    cursor.moveToPosition(position);
    String idBouteille = cursor.getString(cursor.getColumnIndex("img"));
    LinearLayout layoutItem;
    if (convertView == null) {
    	layoutItem = (LinearLayout) mInflater.inflate(R.layout.bottles_list_item, parent, false);
    } else {
    	layoutItem = (LinearLayout) convertView;
    }
    new DownloadFilesTask(layoutItem).execute(idBouteille);
    Après j'y connais rien en optimisation, je ne sais pas exactement comment une listView / gridView fonctionne, comment elle récupère ses vues et tout ça.
    Mais là ça semble plutôt efficace.

    Re Edit :
    En fait j'ai toujours le même problème.
    Je fais pas mal de :
    adapter.changeCursor(cursor);

    Et des ImageView se copient...
    Keith Flint 1969 - 2019

  9. #9
    Modérateur
    Avatar de Hizin
    Homme Profil pro
    Développeur mobile
    Inscrit en
    Février 2010
    Messages
    2 180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

    Informations professionnelles :
    Activité : Développeur mobile

    Informations forums :
    Inscription : Février 2010
    Messages : 2 180
    Points : 5 072
    Points
    5 072
    Par défaut
    Image dans une ListView ?

    Est-ce que tes ImagesView sont réutilisées ? Recréées ? Ou Recyclées ?
    (réutilisées : on garde l'ImageView et on change uniquement son image
    Recréés : on ne garde rien et on recrée tout
    Recyclée : on garde l'image et on recrée l'ImageView)
    J'ai l'impression que tu es dans le cas "réutilisées".

    Explications (inutiles ?) : le fonctionnement optimal des ListView est de créer X lignes, X étant le nombre de ligne affichées, avec quelques unes en rab.
    Ces lignes (et ce qu'elles contiennent) peuvent être réutilisées, cela dépend de l'implémentation.

    J'ai l'impression, pour les affichages multiples, que tu passes les ImageView "représentative" et que tu charges dedans (il est aussi possible que les images se superposent, difficile à voir pour les images, facile pour les textes).
    Les ImageView se mettraient à jour dépendant des téléchargement et la dernière image téléchargées pour l'ImageView passée serait affichée.
    Ce qu'il faudrait ici, c'est un conteneur quelconque pour les images, et dire à la liste "tu montres la ligne Z avec l'image à la place Z de ma liste".

    Un moyen de vérifier si les ImageView sont bien différentes (ou identiques) pour chacun des téléchargement est d'afficher leur adresse mémoire via un toString() basique. Si tu retrouves au moins une fois les mêmes adresses mémoires, je pense que je tape dans le mille, sinon pas loin.

    En espérant ne pas te mettre sur une mauvaise piste ...

    HS : mauvais souvenir que le chargement asynchrone d'image dans une ListView x)
    C'est Android, PAS Androïd, ou Androïde didiou !
    Le premier est un OS, le second est la mauvaise orthographe du troisième, un mot français désignant un robot à forme humaine.

    Membre du comité contre la phrase "ça marche PAS" en titre et/ou explication de problème.

    N'oubliez pas de consulter les FAQ Android et les cours et tutoriels Android

  10. #10
    Membre extrêmement actif
    Avatar de Ryu2000
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2008
    Messages
    9 552
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2008
    Messages : 9 552
    Points : 18 446
    Points
    18 446
    Par défaut
    En en fait je ne sais pas exactement ce que je fais...

    Dans le getView de l'Adapter j'envoie une View et un URL à une AsynckTask.

    J'ai provoqué une erreur et affiché les ImageView et ce ne sont pas les mêmes :
    android.widget.ImageView@2afc8aa8
    android.widget.ImageView@2b027d18
    android.widget.ImageView@2b02d930
    Keith Flint 1969 - 2019

  11. #11
    Modérateur
    Avatar de Hizin
    Homme Profil pro
    Développeur mobile
    Inscrit en
    Février 2010
    Messages
    2 180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

    Informations professionnelles :
    Activité : Développeur mobile

    Informations forums :
    Inscription : Février 2010
    Messages : 2 180
    Points : 5 072
    Points
    5 072
    Par défaut
    Les 3 adresses mémoires que tu viens de mettre sont pour 3 ImageView ayant la même image ?

    Si tu ne scrolls pas, que se passe-t-il ? J'entends pas là : attente de tout les affichages à l'écran, PUIS bouger pour en déclencher quelques autres jusqu'à tous les déclencher petit à petit.
    C'est Android, PAS Androïd, ou Androïde didiou !
    Le premier est un OS, le second est la mauvaise orthographe du troisième, un mot français désignant un robot à forme humaine.

    Membre du comité contre la phrase "ça marche PAS" en titre et/ou explication de problème.

    N'oubliez pas de consulter les FAQ Android et les cours et tutoriels Android

  12. #12
    Membre extrêmement actif
    Avatar de Ryu2000
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2008
    Messages
    9 552
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2008
    Messages : 9 552
    Points : 18 446
    Points
    18 446
    Par défaut
    En fait elles sont sensé avoir 3 images différentes, mais 2 d'entre elles on la même image.

    En fait quand j'actualise l'affiche en changeant l'orientation ou en scrollant par exemple ça fonctionne.

    Mais l'erreur se produit quand je fais plein de changeCursor, j'ai fais une vidéo :
    http://youtu.be/55xVmbEuUO0

    Edit :
    J'ai trouvé un gars qui avait le même problème que moi :
    http://stackoverflow.com/questions/7...es-in-listview

    Si je n'utilise pas la convertView ça fonctionne, mais bon je ne recycle pas la vue. (c'est ça ?)

    Re Edit :
    Purée en testant j'étais presque arriver à ce point, mais je ne comprenais pas le truc et j'avais l'impression que ce que je faisais était stupide, mais en fait non !

    Dans le getView :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    imageView.setTag(adresseDeLImage);
    new asyncTaskQuiAfficheLImage(imageView).execute();
    Dans l'asyncTask :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    String imageView;
    String path;
    Dans le constructeur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    this.imageView = imageView;
    path = imageView.getTag().toString();
    Et dans le postExecute :
    Si le tag de l'ImageView est différent de Path on return;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    if(!imageView.getTag().toString().equals(path)){
         return ;
    }
    Bon ben voilà c'est re résolu !
    Merci
    Keith Flint 1969 - 2019

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 10
    Dernier message: 18/05/2015, 16h59
  2. Problème de mémoire avec Bitmap (c++/cli)
    Par bonofred dans le forum Windows Forms
    Réponses: 9
    Dernier message: 21/01/2009, 16h58
  3. Problème en mémoire avec des tableaux
    Par dword2add dans le forum C++
    Réponses: 3
    Dernier message: 11/11/2007, 14h36
  4. Réponses: 6
    Dernier message: 19/10/2004, 14h46
  5. Problème de mémoire avec BDE
    Par Machuet dans le forum Bases de données
    Réponses: 3
    Dernier message: 13/07/2004, 11h11

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo