web-dev-qa-db-fra.com

Android la mise en cache des images

Comment puis-je mettre en cache des images après leur téléchargement à partir du Web?

139
d-man

Et maintenant, le punchline: utilisez le cache système.

URL url = new URL(strUrl);
URLConnection connection = url.openConnection();
connection.setUseCaches(true);
Object response = connection.getContent();
if (response instanceof Bitmap) {
  Bitmap bitmap = (Bitmap)response;
} 

Fournit à la fois la mémoire cache et la mémoire cache flash-rom, partagée avec le navigateur.

grr. J'aurais aimé que quelqu'un me l'ait dit avant d'écrire mon propre gestionnaire de cache.

173
edrowland

En ce qui concerne la solution élégante connection.setUseCaches Ci-dessus: malheureusement, cela ne fonctionnera pas sans un effort supplémentaire. Vous devrez installer un ResponseCache en utilisant ResponseCache.setDefault. Sinon, HttpURLConnection ignorera en silence le bit setUseCaches(true).

Voir les commentaires en haut de FileResponseCache.Java Pour plus de détails:

http://libs-for-Android.googlecode.com/svn/reference/com/google/Android/filecache/FileResponseCache.html

(Je posterais ceci dans un commentaire, mais apparemment je n'en ai pas assez SO karma.)

65
Joe

Convertissez-les en bitmaps, puis stockez-les dans une collection (HashMap, List, etc.) ou écrivez-les sur la carte SD.

Lorsque vous les stockez dans l’espace d’application à l’aide de la première approche, vous pouvez les regrouper autour de Java.lang.ref.SoftReference spécifiquement si leur nombre est élevé (afin qu’ils soient collectés lors de la crise). Cela pourrait cependant entraîner un rechargement.

HashMap<String,SoftReference<Bitmap>> imageCache =
        new HashMap<String,SoftReference<Bitmap>>();

les écrire sur une carte SD ne nécessitera pas de rechargement; juste une permission d'utilisateur.

27
Samuh

Utilisez LruCache pour mettre les images en cache efficacement. Vous pouvez en savoir plus sur LruCache sur site de développement Android

J'ai utilisé la solution ci-dessous pour le téléchargement d'images et la mise en cache sous Android. Vous pouvez suivre les étapes ci-dessous:

ÉTAPE 1: Rendez la classe nommée ImagesCache. J'ai utilisé Singleton object for this class

import Android.graphics.Bitmap;
import Android.support.v4.util.LruCache;

public class ImagesCache 
{
    private  LruCache<String, Bitmap> imagesWarehouse;

    private static ImagesCache cache;

    public static ImagesCache getInstance()
    {
        if(cache == null)
        {
            cache = new ImagesCache();
        }

        return cache;
    }

    public void initializeCache()
    {
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() /1024);

        final int cacheSize = maxMemory / 8;

        System.out.println("cache size = "+cacheSize);

        imagesWarehouse = new LruCache<String, Bitmap>(cacheSize)
                {
                    protected int sizeOf(String key, Bitmap value) 
                    {
                        // The cache size will be measured in kilobytes rather than number of items.

                        int bitmapByteCount = value.getRowBytes() * value.getHeight();

                        return bitmapByteCount / 1024;
                    }
                };
    }

    public void addImageToWarehouse(String key, Bitmap value)
    {       
        if(imagesWarehouse != null && imagesWarehouse.get(key) == null)
        {
            imagesWarehouse.put(key, value);
        }
    }

    public Bitmap getImageFromWarehouse(String key)
    {
        if(key != null)
        {
            return imagesWarehouse.get(key);
        }
        else
        {
            return null;
        }
    }

    public void removeImageFromWarehouse(String key)
    {
        imagesWarehouse.remove(key);
    }

    public void clearCache()
    {
        if(imagesWarehouse != null)
        {
            imagesWarehouse.evictAll();
        }       
    }

}

ÉTAPE 2:

créez une autre classe nommée DownloadImageTask qui est utilisée si bitmap n'est pas disponible en cache, il sera téléchargé à partir d'ici:

public class DownloadImageTask extends AsyncTask<String, Void, Bitmap>
{   
    private int inSampleSize = 0;

    private String imageUrl;

    private BaseAdapter adapter;

    private ImagesCache cache;

    private int desiredWidth, desiredHeight;

    private Bitmap image = null;

    private ImageView ivImageView;

    public DownloadImageTask(BaseAdapter adapter, int desiredWidth, int desiredHeight) 
    {
        this.adapter = adapter;

        this.cache = ImagesCache.getInstance();

        this.desiredWidth = desiredWidth;

        this.desiredHeight = desiredHeight;
    }

    public DownloadImageTask(ImagesCache cache, ImageView ivImageView, int desireWidth, int desireHeight)
    {
        this.cache = cache;

        this.ivImageView = ivImageView;

        this.desiredHeight = desireHeight;

        this.desiredWidth = desireWidth;
    }

    @Override
    protected Bitmap doInBackground(String... params) 
    {
        imageUrl = params[0];

        return getImage(imageUrl);
    }

    @Override
    protected void onPostExecute(Bitmap result) 
    {
        super.onPostExecute(result);

        if(result != null)
        {
            cache.addImageToWarehouse(imageUrl, result);

            if(ivImageView != null)
            {
                ivImageView.setImageBitmap(result);
            }
            else
            {

            }

            if(adapter != null)
            {
                adapter.notifyDataSetChanged();
            }
        }
    }

    private Bitmap getImage(String imageUrl)
    {   
        if(cache.getImageFromWarehouse(imageUrl) == null)
        {
            BitmapFactory.Options options = new BitmapFactory.Options();

            options.inJustDecodeBounds = true;

            options.inSampleSize = inSampleSize;

            try
            {
                URL url = new URL(imageUrl);

                HttpURLConnection connection = (HttpURLConnection)url.openConnection();

                InputStream stream = connection.getInputStream();

                image = BitmapFactory.decodeStream(stream, null, options);

                int imageWidth = options.outWidth;

                int imageHeight = options.outHeight;

                if(imageWidth > desiredWidth || imageHeight > desiredHeight)
                {   
                    System.out.println("imageWidth:"+imageWidth+", imageHeight:"+imageHeight);

                    inSampleSize = inSampleSize + 2;

                    getImage(imageUrl);
                }
                else
                {   
                    options.inJustDecodeBounds = false;

                    connection = (HttpURLConnection)url.openConnection();

                    stream = connection.getInputStream();

                    image = BitmapFactory.decodeStream(stream, null, options);

                    return image;
                }
            }

            catch(Exception e)
            {
                Log.e("getImage", e.toString());
            }
        }

        return image;
    }

ÉTAPE 3: Utilisation de votre Activity ou Adapter

Remarque: Si vous souhaitez charger une image à partir de l'URL à partir de Activity Class. Utilisez le second constructeur de DownloadImageTask, mais si vous souhaitez afficher une image de Adapter, utilisez le premier constructeur de DownloadImageTask (par exemple, vous avez une image dans ListView et vous définissez l'image à partir de 'Adaptateur')

UTILISATION DE L'ACTIVITÉ:

ImageView imv = (ImageView) findViewById(R.id.imageView);
ImagesCache cache = ImagesCache.getInstance();//Singleton instance handled in ImagesCache class.
cache.initializeCache();

String img = "your_image_url_here";

Bitmap bm = cache.getImageFromWarehouse(img);

if(bm != null)
{
  imv.setImageBitmap(bm);
}
else
{
  imv.setImageBitmap(null);

  DownloadImageTask imgTask = new DownloadImageTask(cache, imv, 300, 300);//Since you are using it from `Activity` call second Constructor.

  imgTask.execute(img);
}

UTILISATION DE L'ADAPTATEUR:

ImageView imv = (ImageView) rowView.findViewById(R.id.imageView);
ImagesCache cache = ImagesCache.getInstance();
cache.initializeCache();

String img = "your_image_url_here";

Bitmap bm = cache.getImageFromWarehouse(img);

if(bm != null)
{
  imv.setImageBitmap(bm);
}
else
{
  imv.setImageBitmap(null);

  DownloadImageTask imgTask = new DownloadImageTask(this, 300, 300);//Since you are using it from `Adapter` call first Constructor.

  imgTask.execute(img);
}

Remarque:

cache.initializeCache() vous pouvez utiliser cette instruction dans la toute première activité de votre application. Une fois que vous avez initialisé le cache, vous n’avez jamais besoin de l’initialiser à chaque fois si vous utilisez l’instance ImagesCache.

Je ne suis jamais doué pour expliquer les choses, mais j'espère que cela aidera les débutants à comprendre comment utiliser le cache en utilisant LruCache et son utilisation :)

EDIT:

De nos jours, il existe des bibliothèques très célèbres connues sous le nom de Picasso et Glide qui peuvent être utilisées pour charger des images très efficacement dans Android app. Essayez ceci très bibliothèque utile Picasso pour Android et Glide For Android Vous n'avez pas à vous soucier des images en cache.

Picasso permet de charger des images sans problème dans votre application, souvent en une seule ligne de code!

Tout comme Picasso, Glide peut charger et afficher des images provenant de nombreuses sources, tout en prenant en charge la mise en cache et en conservant un faible impact sur la mémoire lors des manipulations d'images. Il a été utilisé par les applications Google officielles (comme celle de Google I/O 2015) et est tout aussi populaire que Picasso. Dans cette série, nous allons explorer les différences et les avantages de Glide over Picasso.

Vous pouvez également visiter le blog pour différence entre Glide et Picasso

26
Zubair Ahmed

Pour télécharger une image et la sauvegarder sur la carte mémoire, procédez comme suit.

//First create a new URL object 
URL url = new URL("http://www.google.co.uk/logos/holiday09_2.gif")

//Next create a file, the example below will save to the SDCARD using JPEG format
File file = new File("/sdcard/example.jpg");

//Next create a Bitmap object and download the image to bitmap
Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());

//Finally compress the bitmap, saving to the file previously created
bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream(file));

N'oubliez pas d'ajouter l'autorisation Internet à votre manifeste:

<uses-permission Android:name="Android.permission.INTERNET" />
17
Ljdawson

J'envisagerais d'utiliser le cache d'images de droidfu. Il implémente un cache d'images en mémoire et sur disque. Vous obtenez également un WebImageView qui tire parti de la bibliothèque ImageCache.

Voici la description complète de droidfu et WebImageView: http://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryadapter/

13
esilver

Comme Thunder Rabbit l'a suggéré, ImageDownloader est le meilleur pour ce travail. J'ai aussi trouvé une légère variation de la classe à:

http://theandroidcoder.com/utilities/Android-image-download-and-caching/

La principale différence entre les deux réside dans le fait qu'ImageDownloader utilise le système de mise en cache Android), et la version modifiée utilise la mémoire interne et externe comme mémoire cache, conservant les images en cache indéfiniment ou jusqu'à ce que l'utilisateur les supprime manuellement. L'auteur mentionne également la compatibilité de Android 2.1.

9
EZFrag

J'ai essayé SoftReferences, ils sont récupérés de manière trop agressive dans Android que j’ai pensé qu’il était inutile de les utiliser.

9
2cupsOfTech

C'est une bonne prise par Joe. L'exemple de code ci-dessus présente deux problèmes - l'un - l'objet de réponse n'est pas une instance de Bitmap (lorsque mon URL fait référence à un jpg, tel que http:\website.com\image.jpg, il s'agit d'un

org.Apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl $ LimitedInputStream).

Deuxièmement, comme le souligne Joe, aucune mise en cache ne se produit sans la configuration d’un cache de réponse. Android doivent utiliser leur propre cache. Voici un exemple, mais caches uniquement en mémoire, ce qui n’est vraiment pas la solution complète.

http://codebycoffee.com/2010/06/29/using-responsecache-in-an-Android-app/

L'API de mise en cache URLConnection est décrite ici:

http://download.Oracle.com/javase/6/docs/technotes/guides/net/http-cache.html

Je pense toujours que c’est une solution acceptable pour emprunter cette voie - mais vous devez quand même écrire un cache. Cela semble amusant, mais je préférerais écrire des fonctionnalités.

7
Peter Pascale

Il existe une entrée spéciale dans la section d’entraînement officielle de Android à ce sujet: http://developer.Android.com/training/displaying-bitmaps/cache-bitmap.html

La section est assez nouvelle, elle n'était pas là quand la question a été posée.

La solution suggérée consiste à utiliser un LruCache. Cette classe a été introduite sur Honeycomb, mais elle est également incluse dans la bibliothèque de compatibilité.

Vous pouvez initialiser un LruCache en définissant le nombre maximal d'entrées. Il les triera automatiquement et les nettoiera automatiquement si vous dépassez la limite. Autre que cela, il est utilisé comme une carte normale.

Le code exemple de la page officielle:

private LruCache mMemoryCache;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // Get memory class of this device, exceeding this amount will throw an
    // OutOfMemory exception.
    final int memClass = ((ActivityManager) context.getSystemService(
            Context.ACTIVITY_SERVICE)).getMemoryClass();

    // Use 1/8th of the available memory for this memory cache.
    final int cacheSize = 1024 * 1024 * memClass / 8;

    mMemoryCache = new LruCache(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            // The cache size will be measured in bytes rather than number of items.
            return bitmap.getByteCount();
        }
    };
    ...
}

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, bitmap);
    }
}

public Bitmap getBitmapFromMemCache(String key) {
    return mMemoryCache.get(key);
}

Auparavant, SoftReferences était une bonne alternative, mais plus maintenant, citant la page officielle:

Remarque: Auparavant, une implémentation de cache mémoire populaire était un cache bitmap SoftReference ou WeakReference, mais cela n'est pas recommandé. À partir de Android 2.3 (API de niveau 9), le ramasse-miettes est plus agressif, car il collecte des références souples/faibles, ce qui les rend relativement inefficaces. De plus, avant Android = 3.0 (API de niveau 11), les données de sauvegarde d'un bitmap ont été stockées dans une mémoire native qui n'est pas publiée de manière prévisible, ce qui peut provoquer le dépassement temporaire de la limite de mémoire d'une application et le blocage de l'application.

7
shalafi

Pensez à utiliser Bibliothèque Universal Image Loader par Sergey Tarasevich . Il est livré avec:

  • Chargement d'image multithread. Il vous permet de définir la taille du pool de threads
  • Mise en cache des images en mémoire, sur le système de fichiers et la carte SD de l'appareil.
  • Possibilité d'écouter l'avancement du chargement et les événements de chargement

Universal Image Loader permet gestion de cache détaillée pour les images téléchargées, avec les configurations de cache suivantes:

  • UsingFreqLimitedMemoryCache: le moins souvent l'image utilisée est supprimée lorsque la taille limite du cache est dépassée.
  • LRULimitedMemoryCache: le moins récemment l'image utilisée est supprimée lorsque la taille limite du cache est dépassée.
  • FIFOLimitedMemoryCache: La règle FIFO est utilisée pour la suppression lorsque la taille limite du cache est dépassée.
  • LargestLimitedMemoryCache: le le plus grand bitmap est supprimé lorsque la limite de taille du cache est dépassée.
  • LimitedAgeMemoryCache: L'objet mis en cache est supprimé lorsque son l'âge dépasse la valeur définie.
  • WeakMemoryCache: cache mémoire contenant uniquement des références faibles à des bitmaps.

Un exemple d'utilisation simple:

ImageView imageView = groupView.findViewById(R.id.imageView);
String imageUrl = "http://site.com/image.png"; 

ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.init(ImageLoaderConfiguration.createDefault(context));
imageLoader.displayImage(imageUrl, imageView);

Cet exemple utilise la valeur par défaut UsingFreqLimitedMemoryCache.

3
Gunnar Karlsson

Ce qui a réellement fonctionné pour moi a été de définir ResponseCache sur mon cours principal:

try {
   File httpCacheDir = new File(getApplicationContext().getCacheDir(), "http");
   long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
   HttpResponseCache.install(httpCacheDir, httpCacheSize);
} catch (IOException e) { } 

et

connection.setUseCaches(true);

lors du téléchargement de bitmap.

http://practicaldroid.blogspot.com/2013/01/utilizing-http-response-cache.html

3
ZamPrano

Libs-for-Android de Google possède une bibliothèque Nice pour gérer le cache des images et des fichiers.

http://code.google.com/p/libs-for-Android/

2
Taoufix

Je luttais avec cela depuis un certain temps; les réponses utilisant SoftReferences perdraient leurs données trop rapidement. Les réponses suggérant l'instanciation d'un RequestCache étaient trop compliquées, et je n'ai jamais pu trouver un exemple complet.

Mais ImageDownloader.Java fonctionne à merveille pour moi. Il utilise une carte de hachage jusqu'à ce que la capacité soit atteinte ou jusqu'à ce que le délai de purge soit atteint, puis les choses sont déplacées vers un SoftReference, utilisant ainsi le meilleur des deux mondes.

1
Thunder Rabbit

Je suggère [~ # ~] d'allumage [~ # ~] c'est encore mieux que Droid fu

https://github.com/kaeppler/ignition

https://github.com/kaeppler/ignition/wiki/Sample-applications

1
Jorgesys

Réponse tardive, mais je me suis dit que je devrais ajouter un lien vers mon site car j'ai écrit un tutoriel sur la création d'un cache d'images pour Android: http://squarewolf.nl/2010/11/Android-image-cache/  Mise à jour: la page a été mise hors connexion, car la source était obsolète. Je rejoins @elenasys dans son conseil d’utiliser Ignition .

Donc, à tous ceux qui trébuchent sur cette question et n’ont pas trouvé de solution: espérons que vous apprécierez! = D

0
Thomas Vervest

Même plus tard, j'ai écrit un Android Image Manager qui gère la mise en cache de manière transparente (mémoire et disque). Le code est sur Github https://github.com/felipecsl/Android -ImageManager

0
Felipe Lima