使用Picasso与自定义磁盘caching

Volley库中, NetworkImageView类需要一个ImageLoader ,通过在ImageCache实现中search它们来处理所有的图像请求,用户可以自由selectcaching的工作方式,图像的位置和名称。

我从Volley转向Retrofit ,而我决定尝试Picasso的形象。

在以前的库中,我在每个包含图像URL的项目中都有一个String参数,然后使用myNetworkImageView.setImageUrl(item.getURL()) ,它能够确定图像是否caching在磁盘上。 如果图像存在于caching文件夹中,则加载图像,否则会下载并加载图像。

我希望能够像毕加索一样做,是否可以用Picasso API或者我自己编码这样的function?

我正在考虑将图像下载到一个文件夹(caching文件夹),完成后使用Picasso.with(mContext).load(File downloadedimage) 。 这是正确的方式,还是有任何最佳做法?

毕加索没有磁盘caching。 它委托给你正在使用的HTTP客户端(依靠HTTPcaching语义进行caching控制)。 正因为如此,你所寻求的行为是免费的。

如果底层HTTP客户端不存在于其本地caching中(并且该映像未过期),则底层HTTP客户端将只通过networking下载图像。

也就是说,您可以java.net.HttpUrlConnection (通过ResponseCache或OkHttp(通过ResponseCacheOkResponseCache )创build自定义的caching实现,它以所需的格式存储文件,但是我强烈build议不 OkResponseCache

让毕加索和HTTP客户端为您做好工作!

您可以在Picasso实例上调用setIndicatorsEnabled(true)以查看图像加载位置的指示符。 它看起来像这样:

如果您永远不会看到蓝色指示符,则可能是您的远程映像不包含正确的caching标头来启用对磁盘的caching。

如果您的项目使用的是okhttp库,那么picasso会自动使用它作为默认的下载器,磁盘caché将自动工作。

假设您使用Android Studio,只需在build.gradle文件的dependencies下添加这两行,您就可以进行设置。 (没有额外的configuration需要毕加索)

 dependencies { [...] compile 'com.squareup.okhttp:okhttp:2.+' compile 'com.squareup.okhttp:okhttp-urlconnection:2.+' } 

正如许多人在这里指出的,OkHttpClient是caching的方式。

当使用OkHttp进行caching时,您可能还想使用OkHttp 拦截器获得对HTTP响应中Cache-Control头的更多控制,请参阅我的回复

以前是怎么写的,毕加索使用底层Http客户端的caching。

HttpUrlConnection的内置caching不能在真正的离线模式下工作。 如果OkHttpClient的使用由于某些原因而不需要 ,可以使用自己的磁盘caching实现(当然是基于DiskLruCache )。

其中一种方法是在com.squareup.picasso.UrlConnectionDownloader和programm整个逻辑子类:

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

然后使用你的实现像这样:

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

这里是我的UrlConnectionDownloader实现,它可以与磁盘caching一起工作,即使在完全离线模式下也可以发送给毕加索位图:

 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); } }