Downloader是网络图片下载器。
Downloader.Response
Response是Downloader的下载结果。
class Response { final InputStream stream; //图片的字节流,和Bitmap只能二选一 final Bitmap bitmap; //图片的位图,和InputStream只能二选一 final boolean cached; //是否是读取磁盘缓存 final long contentLength; //stream的长度}复制代码
自定义Downloader
Downloader是一个接口。自定义Downloader需要实现两个方法。
public interface Downloader { //根据Uri和网络策略下载图片 @Nullable Response load(@NonNull Uri uri, int networkPolicy) throws IOException; //关闭下载器,包括关闭磁盘缓存和其他资源 void shutdown();}复制代码
然后把自定义的Downloader通过Picasso.Builder的downloader方法添加到Picasso中。
默认的Downloader
当没有提供自定义Downloader时,Picasso会调用Utils.createDefaultDownloader方法创建默认的Downloader。
static Downloader createDefaultDownloader(Context context) { if (SDK_INT >= GINGERBREAD) { //先判断是否使用了okhttp3,如果有则使用OkHttp3Downloader try { Class.forName("okhttp3.OkHttpClient"); return OkHttp3DownloaderCreator.create(context); } catch (ClassNotFoundException ignored) { } //判断是否使用了okhttp,如果有则使用OkHttpDownloader try { Class.forName("com.squareup.okhttp.OkHttpClient"); return OkHttpDownloaderCreator.create(context); } catch (ClassNotFoundException ignored) { } } //如果没有okhttp,则使用UrlConnectionDownloader return new UrlConnectionDownloader(context);}复制代码
OkHttpDownloader和OkHttp3Downloader
OkHttpDownloader和OkHttp3Downloader的区别只是由于okhttp版本不同而导致的客户端创建方式不同而已。下面以OkHttp3Downloader进行说明。 Picasso的Utils为OkHttp3指定了默认的缓存路径和缓存大小。
//缓存路径是应用缓存目录下的picasso-cache文件夹static File createDefaultCacheDir(Context context) { File cache = new File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE); if (!cache.exists()) { cache.mkdirs(); } return cache;}static long calculateDiskCacheSize(File dir) { long size = MIN_DISK_CACHE_SIZE; try { StatFs statFs = new StatFs(dir.getAbsolutePath()); long blockCount = SDK_INT < JELLY_BEAN_MR2 ? (long) statFs.getBlockCount() : statFs.getBlockCountLong(); long blockSize = SDK_INT < JELLY_BEAN_MR2 ? (long) statFs.getBlockSize() : statFs.getBlockSizeLong(); long available = blockCount * blockSize; //缓存大小是缓存目录磁盘可用空间的2% size = available / 50; } catch (IllegalArgumentException ignored) { } return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE);}复制代码
使用OkHttp获取网络图片是比较简单的。
@Override public Response load(@NonNull Uri uri, int networkPolicy) throws IOException { //根据网络策略设置CacheControl CacheControl cacheControl = null; if (networkPolicy != 0) { if (NetworkPolicy.isOfflineOnly(networkPolicy)) {强制读取缓存 cacheControl = CacheControl.FORCE_CACHE; } else { CacheControl.Builder builder = new CacheControl.Builder(); if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) { //不先检查缓存是否有目标文件 builder.noCache(); } if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) { //请求结果不进行缓存 builder.noStore(); } cacheControl = builder.build(); } } Request.Builder builder = new okhttp3.Request.Builder().url(uri.toString()); if (cacheControl != null) { builder.cacheControl(cacheControl); } okhttp3.Response response = client.newCall(builder.build()).execute(); int responseCode = response.code(); if (responseCode >= 300) { response.body().close(); throw new ResponseException(responseCode + " " + response.message(), networkPolicy, responseCode); } boolean fromCache = response.cacheResponse() != null; //判断是读取磁盘缓存还是读取网络 ResponseBody responseBody = response.body(); return new Response(responseBody.byteStream(), fromCache, responseBody.contentLength()); } @Override public void shutdown() { if (!sharedClient) { //如果缓存目录不是共享的,则关闭缓存 if (cache != null) { try { cache.close(); } catch (IOException ignored) { } } }}复制代码
UrlConnectionDownloader
UrlConnectionDownloader是通过HttpURLConnection来进行网络请求的。缓存路径、缓存大小、CacheControl设置和OkHttpDownloader是一致的。有区别的一点是,UrlConnectionDownloader发送网络请求是单线程的,所以创建缓存目录时需要加锁避免重复创建。设置CacheControl的字符串使用了ThreadLocal,来保证数据的准确性。