Picasso源码阅读笔记八

本文介绍Picasso中图片加载器Downloader的工作原理,包括其下载结果Response的结构、自定义Downloader的方法、默认Downloader的选择机制,以及使用OkHttp和UrlConnection进行图片下载的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值