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,来保证数据的准确性。