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