Android:图片加载框架Picasso源码分析(Picasso 2.71828)

Picasso 是 Android 开发中最受欢迎的图片请求加载框架之一 ,它诞生于 2013 年,距今已有六年的生命。在这六年间 Picasso 发布过 21 个版本更新,而最近的一次更新为18年的 3 月 8 日,更新的版本号为 2.71828(文中统称为新版),该版本离上一次发布更新相隔了三年。本文主要分析新版 Picasso 的源码实现和它的一些 API 变化。

本文过长,推荐关注我之后再慢慢品尝~

1. 新版 Picasso 的使用

新版 Picasso 最直观的变化就是在 App 中的调用方式为:

Picasso.get().load(url).into(imageView);

该调用跟原来版本的调用区别是,没有了需要传 Context 的 with 方法,取而代之是一个不需要传参的 get 方法来获取全局唯一 Picasso 实例。

本文也主要通过分析 Picasso.get().load(url).into(imageView) 该句调用的来龙去脉来理清新版 Picasso 框架的实现原理。

2. Picasso 实例的获取

Picasso 实例是通过调用 Picasso 类中的静态方法 get 获取的,该方法也是新版 Picasso 的入口,我们从该方法开始看起:

static volatile Picasso singleton = null;

public static Picasso get() {
  if (singleton == null) {
    synchronized (Picasso.class) {
      if (singleton == null) {
        if (PicassoProvider.context == null) {
          throw new IllegalStateException("context == null");
        }
        singleton = new Builder(PicassoProvider.context).build();
      }
    }
  }
  return singleton;
}

上面这段代码是一个非常经典的双重检查锁模式 (Double Checked Locking Pattern)。

首先进入 get 方法即检查一次 Picasso 实例 singleton 是否为 null,不为 null 就可以直接返回该实例。

接着进入到同步块,因为可能会一个线程进入同步块后创建完对象后退出,另一个线程又紧接着进入同步块,因此进入同步块后需要再检查一次 singleton 是否为 null,如果还为 null,这时候可以开始初始化该实例。

最后静态变量 singleton 需要用 volatile 关键字来修饰,目的是为了防止重排序。

2.1 使用 ContentProvider 获取 Context

新版 Picasso 提供给我们的入口方法 get 不需要传 Context,因为它使用的是 PicassoProvider 类中的 context,我们看一下 PicassoProvider 类:

public final class PicassoProvider extends ContentProvider {

  @SuppressLint("StaticFieldLeak") static Context context;

  @Override public boolean onCreate() {
    context = getContext();
    return true;
  }

}

PicassoProvider 类继承自 ContentProvider,除了 onCreate 方法,其他方法都是默认实现 (为了节省篇幅,省略了该部分代码),而 onCreate 方法也只是调用 getContext 方法并赋值给静态变量 context,然后返回 true 表示成功加载了该 ContentProvider。

Picasso 这么做的理由是,只要将 PicassoProvider 在 AndroidManifest 文件中注册,那么 App 在启动的时候,系统就会自动回调 PicassoProvider 的 onCreate 方法,因此也就自动获取到了 Context。

2.2 Picasso 实例的创建

接着回到 Picasso.get 方法中,Picasso 实例通过该句代码创建:

singleton = new Builder(PicassoProvider.context).build();

这里使用到了常用的 Builder 设计模式。当一个类的属性过多,通过构造函数构造一个对象过于复杂时,可以选择使用 Builder 设计模式来简化对象的构造过程。

看下 Picasso 类中静态内部类 Builder 的构造函数:

public Builder(@NonNull Context context) {
  if (context == null) {
    throw new IllegalArgumentException("Context must not be null.");
  }
  this.context = context.getApplicationContext();
}

该构造函数确保传进来的 Context 实例不为 null,然后获取全局的 Application Context。

接着是 Picasso.Builder 类中的 build 方法:

private final Context context;
private Downloader downloader;
private ExecutorService service;
private Cache cache;
private Listener listener;
private RequestTransformer transformer;
private List<RequestHandler> requestHandlers;
private Bitmap.Config defaultBitmapConfig;

public Picasso build() {
  Context context = this.context;
  // 配置下载器 Downloader,用于从网络下载图片资源,默认为 OkHttp3Downloader
  if (downloader == null) {
    downloader = new OkHttp3Downloader(context);
  }
  // 配置缓存 Cache,用来保存最近查看使用的图片,默认为 LruCache
  if (cache == null) {
    cache = new LruCache(context);
  }
  // 配置 ExecutorService,默认为 PicassoExecutorService
  // 后面 Bitmap 的获取任务就在该线程池中完成
  if (service == null) {
    service = new PicassoExecutorService();
  }
  // 配置 RequestTransformer 实例
  if (transformer == null) {
    transformer = RequestTransformer.IDENTITY;
  }
  // 创建 Stats 实例,Stats 类用来进行一些统计,如缓存命中数,图片下载数等
  Stats stats = new Stats(cache);
  // 创建 Dispatcher 实例,Dispatcher 类顾名思义,它的作用就是用来分发处理
  // 各种图片操作事件的如提交图片请求事件,图片获取完成事件等;
  // 传入前面配置好的对象和 HANDLER 实例给 Dispatcher 类构造函数
  // 该 HANDLER 在主线程接收处理事件,后面获取到 Bitmap 后需要回调到
  // 该 HANDLER 的 handleMessage 方法中以便将 Bitmap 切换回主线程显示
  Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
  // 传入前面配置好的一系列参数,创建 Picasso 实例
  return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
      defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}

该方法的逻辑与相关类作用已在注释中进行了说明。

接下来看 Picasso 类的构造函数:

private final Listener listener;
private final RequestTransformer requestTransformer;
private final CleanupThread cleanupThread;
private final List<RequestHandler> requestHandlers;

final Context context;
final Dispatcher dispatcher;
final Cache cache;
final Stats stats;
final Map<Object, Action> targetToAction;
final Map<ImageView, DeferredRequestCreator> targetToDeferredRequestCreator;
final ReferenceQueue<Object> referenceQueue;
final Bitmap.Config defaultBitmapConfig;

boolean indicatorsEnabled;
volatile boolean loggingEnabled;

boolean shutdown;

Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener, RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats,Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {
  // 一些赋值操作
  this.context = context;
  this.dispatcher = dispatcher;
  this.cache = cache;
  this.listener = listener;
  this.requestTransformer = requestTransformer;
  this.defaultBitmapConfig = defaultBitmapConfig;
  // Picasso 默认包含七个内置 RequestHandler 分别用来处理七种不同类型的请求
  // 你也可以自己继承 RequestHandler 类来处理你的自定义请求
  // 自定义请求放在 extraRequestHandlers 中
  int builtInHandlers = 7;
  int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
  List<RequestHandler> allRequestHandlers = new ArrayList<>(builtInHandlers + extraCount);
  // 添加 ResourceRequestHandler,用于处理加载图片资源 id 的情况
  // ResourceRequestHandler 需要第一个进行添加
  // 避免其他的 RequestHandler 检查 (request.resourceId != 0) 的情况
  allRequestHandlers.add(new ResourceRequestHandler(context));
  // 然后添加自定义的 RequestHandler (如果有的话)
  if (extraRequestHandlers != null) {
    allRequestHandlers.addAll(extraRequestHandlers);
  }
  // 添加 ContactsPhotoRequestHandler,用于处理手机联系人图片
  allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
  // 添加 MediaStoreRequestHandler,用于处理 content://media/ 开头的 URI
  allRequestHandlers.add(new MediaStoreRequestHandler(context));
  // 添加 ContentStreamRequestHandler,用于处理 scheme 为 content 的 URI
  allRequestHandlers.add(new ContentStreamRequestHandler(context));
  // 添加 AssetRequestHandler,用于处理 file:///android_asset/ 开头的 URI
  allRequestHandlers.add(new AssetRequestHandler(context));
  // 添加 FileRequestHandler,用于处理 scheme 为 file 的 URI
  allRequestHandlers.add(new FileRequestHandler(context));
  // 添加 NetworkReques
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值