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?
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.
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.+'
}
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
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);
}
}