Utiliser Picasso avec un cache disque personnalisé

dans Volley , la classe NetworkImageView nécessite une ImageLoader qui gère toutes les requêtes d'image en les recherchant dans une implémentation ImageCache , l'utilisateur est libre de choisir comment le cache doit fonctionner, l'emplacement et le nom des images.

je passe de Volley à Retrofit , et pour les images j'ai décidé d'essayer Picasso .

avec l'ancienne bibliothèque, j'avais un paramètre String chacun de mes éléments contenant L'URL de l'image, puis j'ai utilisé myNetworkImageView.setImageUrl(item.getURL()) et il a été en mesure de déterminer si l'image était mise en cache sur le disque. Si l'image existait dans le dossier cache, l'image était chargée, sinon elle était téléchargée et chargée.

je voudrais pouvoir faire la même chose avec Picasso, est-ce possible avec Picasso APIs ou dois-je coder une telle caractéristique par moi-même?

je pensais télécharger l'image dans un dossier (le dossier cache), et utiliser Picasso.with(mContext).load(File downloadedimage) à l'achèvement. Est-ce la bonne façon ou il y a des pratiques exemplaires?

22
demandé sur Vektor88 2014-04-25 01:29:48

4 réponses

Picasso n'a pas de cache disque. Il délègue à N'importe quel client HTTP que vous utilisez pour cette fonctionnalité (en s'appuyant sur la sémantique de cache HTTP pour le contrôle de cache). Pour cette raison, le comportement que vous recherchez est gratuit.

le client HTTP sous-jacent ne téléchargera une image sur le réseau que si elle n'existe pas dans son cache local (et que l'image n'est pas expirée).

qui dit, vous peut créer cache personnalisé mise en œuvre pour java.net.HttpUrlConnection (via ResponseCache ou OkHttp (via ResponseCache ou OkResponseCache ) qui stocke les fichiers dans le format que vous désirez. je vous déconseille fortement, cependant.

laissez Picasso et le client HTTP faire le travail pour vous!

vous pouvez appeler setIndicatorsEnabled(true) sur l'instance Picasso pour voir un indicateur d'où les images sont chargées. Il on dirait:

si vous ne voyez jamais d'indicateur bleu, il est probable que vos images distantes n'incluent pas les en-têtes de cache appropriés pour activer la mise en cache vers le disque.

47
répondu Jake Wharton 2017-02-08 14:52:52

si votre projet utilise la bibliothèque okhttp alors picasso l'utilisera automatiquement comme téléchargeur par défaut et le cache disque fonctionnera automatiquement.

en supposant que vous utilisez Android Studio, il suffit d'ajouter ces deux lignes sous dependencies dans le fichier build.gradle et vous serez défini. (Aucune configuration supplémentaire avec picasso nécessaire)

dependencies {
    [...]
    compile 'com.squareup.okhttp:okhttp:2.+'
    compile 'com.squareup.okhttp:okhttp-urlconnection:2.+'
}
12
répondu txulu 2014-07-23 11:14:27

comme de nombreuses personnes ici l'ont souligné à juste titre, OkHttpClient est la voie à suivre pour la mise en cache.

lors de la mise en cache avec OkHttp, vous pouvez également vouloir gagner plus de contrôle sur L'en-tête Cache-Control dans la réponse HTTP en utilisant OkHttp intercepteurs , voir ma réponse ici

2
répondu Gaurav B 2017-05-23 11:55:07

comme il est écrit précédemment, Picasso utilise un cache du client Http sous-jacent.

HttpUrlConnection cache intégré ne fonctionne pas vraiment en mode offline et si l'utilisation D'OkHttpClient est indésirable pour certaines raisons, il est possible d'utiliser votre propre implémentation de disk-cache (bien sûr basé sur DiskLruCache ).

l'une des façons est de sous-classer com.squareup.picasso.UrlConnectionDownloader et de programmer la logique entière à:

@Override
public Response load(final Uri uri, int networkPolicy) throws IOException {
...
}

et puis utiliser votre implémentation comme ceci:

new Picasso.Builder(context).downloader(<your_downloader>).build();

Voici mon implémentation de UrlConnectionDownloader , qui fonctionne avec disk-cache et les bateaux à Picasso bitmaps même en mode total offline:

public class PicassoBitmapDownloader extends UrlConnectionDownloader {

    private static final int MIN_DISK_CACHE_SIZE = 5 * 1024 * 1024; // 5MB
    private static final int MAX_DISK_CACHE_SIZE = 50 * 1024 * 1024; // 50MB

    @NonNull private Context context;
    @Nullable private DiskLruCache diskCache;

    public class IfModifiedResponse extends Response {

        private final String ifModifiedSinceDate;

        public IfModifiedResponse(InputStream stream, boolean loadedFromCache, long contentLength, String ifModifiedSinceDate) {

            super(stream, loadedFromCache, contentLength);
            this.ifModifiedSinceDate = ifModifiedSinceDate;
        }

        public String getIfModifiedSinceDate() {

            return ifModifiedSinceDate;
        }
    }

    public PicassoBitmapDownloader(@NonNull Context context) {

        super(context);
        this.context = context;
    }

    @Override
    public Response load(final Uri uri, int networkPolicy) throws IOException {

        final String key = getKey(uri);
        {
            Response cachedResponse = getCachedBitmap(key);
            if (cachedResponse != null) {
                return cachedResponse;
            }
        }

        IfModifiedResponse response = _load(uri);

        if (cacheBitmap(key, response.getInputStream(), response.getIfModifiedSinceDate())) {

            IfModifiedResponse cachedResponse = getCachedBitmap(key);
            if (cachedResponse != null) {return cachedResponse;
            }
        }

        return response;
    }

    @NonNull
    protected IfModifiedResponse _load(Uri uri) throws IOException {

        HttpURLConnection connection = openConnection(uri);

        int responseCode = connection.getResponseCode();
        if (responseCode >= 300) {
            connection.disconnect();
            throw new ResponseException(responseCode + " " + connection.getResponseMessage(),
                    0, responseCode);
        }

        long contentLength = connection.getHeaderFieldInt("Content-Length", -1);
        String lastModified = connection.getHeaderField(Constants.HEADER_LAST_MODIFIED);
        return new IfModifiedResponse(connection.getInputStream(), false, contentLength, lastModified);
    }

    @Override
    protected HttpURLConnection openConnection(Uri path) throws IOException {

        HttpURLConnection conn = super.openConnection(path);

        DiskLruCache diskCache = getDiskCache();
        DiskLruCache.Snapshot snapshot = diskCache == null ? null : diskCache.get(getKey(path));
        if (snapshot != null) {
            String ifModifiedSince = snapshot.getString(1);
            if (!isEmpty(ifModifiedSince)) {
                conn.addRequestProperty(Constants.HEADER_IF_MODIFIED_SINCE, ifModifiedSince);
            }
        }

        return conn;
    }

    @Override public void shutdown() {

        try {
            if (diskCache != null) {
                diskCache.flush();
                diskCache.close();
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }

        super.shutdown();
    }

    public boolean cacheBitmap(@Nullable String key, @Nullable InputStream inputStream, @Nullable String ifModifiedSince) {

        if (inputStream == null || isEmpty(key)) {
            return false;
        }

        OutputStream outputStream = null;
        DiskLruCache.Editor edit = null;
        try {
            DiskLruCache diskCache = getDiskCache();
            edit = diskCache == null ? null : diskCache.edit(key);
            outputStream = edit == null ? null : new BufferedOutputStream(edit.newOutputStream(0));

            if (outputStream == null) {
                return false;
            }

            ChatUtils.copy(inputStream, outputStream);
            outputStream.flush();

            edit.set(1, ifModifiedSince == null ? "" : ifModifiedSince);
            edit.commit();

            return true;
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {

            if (edit != null) {
                edit.abortUnlessCommitted();
            }

            ChatUtils.closeQuietly(outputStream);
        }
        return false;
    }

    @Nullable
    public IfModifiedResponse getCachedBitmap(String key) {

        try {
            DiskLruCache diskCache = getDiskCache();
            DiskLruCache.Snapshot snapshot = diskCache == null ? null : diskCache.get(key);
            InputStream inputStream = snapshot == null ? null : snapshot.getInputStream(0);

            if (inputStream == null) {
                return null;
            }

            return new IfModifiedResponse(inputStream, true, snapshot.getLength(0), snapshot.getString(1));
        }
        catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    @Nullable
    synchronized public DiskLruCache getDiskCache() {

        if (diskCache == null) {

            try {
                File file = new File(context.getCacheDir() + "/images");
                if (!file.exists()) {
                    //noinspection ResultOfMethodCallIgnored
                    file.mkdirs();
                }

                long maxSize = calculateDiskCacheSize(file);
                diskCache = DiskLruCache.open(file, BuildConfig.VERSION_CODE, 2, maxSize);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }

        return diskCache;
    }

    @NonNull
    private String getKey(@NonNull Uri uri) {

        String key = md5(uri.toString());
        return isEmpty(key) ? String.valueOf(uri.hashCode()) : key;
    }

    @Nullable
    public static String md5(final String toEncrypt) {

        try {
            final MessageDigest digest = MessageDigest.getInstance("md5");
            digest.update(toEncrypt.getBytes());
            final byte[] bytes = digest.digest();
            final StringBuilder sb = new StringBuilder();
            for (byte aByte : bytes) {
                sb.append(String.format("%02X", aByte));
            }
            return sb.toString().toLowerCase();
        }
        catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    static long calculateDiskCacheSize(File dir) {

        long available = ChatUtils.bytesAvailable(dir);
        // Target 2% of the total space.
        long size = available / 50;
        // Bound inside min/max size for disk cache.
        return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE);
    }
}
0
répondu Alexander Skvortsov 2016-03-25 12:49:38