如果想用 Picasso 展示一张图片,可以这样调用,只要一行代码就能展示图片了,图片的来源可以是本地,资源文件或者网络。
Picasso.get().load(res).into(imageview)
我们就以此作为切入点分析一下其源码实现逻辑。
get 方法
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;
}
get 方法实际上是单例模式的表现,采用 DCL 的写法实现单例。PicassoProvider 继承自 ContentProvider,不太明白这样获取 Context 对象的用意,这里就认为它提供了上下文。接下去采用了 Builder 模式,在 Picasso 类里有个静态类 Builder,通过它来创建了 Picasso 对象。
有这么几个东西是可配的,也是后面梳理的重点。这就有点像 Retrofit 对象的创建。
- Downloader 定义了从外部加载图片的方式(是磁盘还是网络),它是一个接口,提供了 load 和 shutdown 方法。默认的实现类是 OkHttp3Downloader。
- Cache 定义了内存缓存的方式,就整个库来看默认是以最近最少使用算法为实现原理,它是一个接口,提供了 get, set, size, maxSize, clear, clearKeyUri 这些方法。默认的实现类是 LruCache。
- PicassoExecutorService 定义了线程池,继承自 ThreadPoolExecutor,具体这个线程池用来做什么目前还不清楚,先知道有这么个东西。
- RequestTransformer 应该是定义了图片加载请求变换的方式,它是一个接口,提供了 transformRequest 方法。默认是不做任何变换的,具体实现类应该很多,这个留到后面分析。
- Stats 创建时需要传入内存缓存方式,目前还不知道是什么作用。
由上述几个属性会创建一个 Dispatcher 对象,
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
这里还没了解其作用,就拿 OkHttp 对比来说,有可能的作用是负责图片加载请求任务的管理。其中 HANDLER 入参是 Picasso 类里的一个静态 Handler 类,且消息处理是在 Android 主线程。
最后再结合其他一些属性变量创建了 Picasso 对象并返回。
load 方法
public RequestCreator load(@Nullable Uri uri) {
return new RequestCreator(this, uri, 0);
}
load 方法其实有多种重载,但最终的工作就是创建了一个 RequestCreator 对象,这个 RequestCreator 从名字上看是个请求创建者,这个请求当然就是指图片加载请求,类注释上说可以流畅的创建图片加载请求,来稍微看下流畅怎么体验,学学人家的写法。
RequestCreator(Picasso picasso, Uri uri, int resourceId) {
this.picasso = picasso;
this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
}
这个 data 是 Request 类里面的静态类 Builder,看来还是用了 Builder 模式。光这步可能还不能体现流畅(我估计流畅主要还是指 Builder 模式的运用),先继续往下看。
into 方法
这个方法异步形式将图片加载请求结果展示在控件上,这个控件指定为 ImageView。有一点要注意,为了防止内存泄漏,对 ImageView 对象的引用是弱引用,从而保证 ImageView 对象能够及时被回收避免内存泄漏的情况(这点也是我们在做和页面相关的引用关联时要特别注意的)。
public void into(ImageView target) {
into(target, null);
}
public void into(ImageView target, Callback callback) {
long started = System.nanoTime();
checkMain();
//上面这两句贴出来是想学习下他的写法,
//一个是用来获取 nano 时间,这个时间精度更高,近似 cpu 的时间片精度吧
//一个是用来判断当前线程是否为主线程
//hasImage 用来判断调用 into 方法的时候有没有传入有效的图片资源 Uri
//如果没有,显然就会终止
if (!data.hasImage()) {
picasso.cancelRequest(target);
//省略部分代码....
return;
}
//deferred 感觉是个是否调整尺寸或者适配的控制
//默认应该是 false,如果需要调整,其实也是对尺寸属性的一个赋值
if (deferred) {
int width = target.getWidth();
int height = target.getHeight();
if (width == 0 || height == 0) {
return;
}
data.resize(width, height);
}
//这句比较核心,本质上就是通过 data 变量去 build 出一个 Request 对象
//Request 对象其实是个数据对象,里面存储这图片信息以及变换,展示尺寸等信息
Request request = createRequest(started);
//这个 key 就是用来缓存用的,key 的生成也可以借鉴一下,省的自己只会 UUID.random 的写法
String requestKey = createKey(request);
//这句判断是否先从缓存中读取,如果有就加载展示图片
if (shouldReadFromMemoryCache(memoryPolicy)) {
//通过 picasso 对象,利用前面说的图片 key 去查找图片
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
if (bitmap != null) {
//如果图片找到了,就取消当前的请求,就用找到的这张图片
//所以这里有个细节,就是如果图片的 key 没有变,那么即使图片内容再怎么变,
//图片最终的展示也不会变,所以图片展示要刷新,就要更新 key
picasso.cancelRequest(target);
//设置图片展示,内部本质是调用 ImageView 的 setImageDrawable 方法
setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
//最后如果想监听回调,可以在这里接收到
if (callback != null) {
callback.onSuccess();
}
return;
}
}
//如果缓存里没有图片,就会创建一个 Action 对象发起请求处理吧
Action action = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId, errorDrawable, requestKey, tag, callback, noFade);
picasso.enqueueAndSubmit(action);
}
看来前面说 RequestCreator 流畅还是指的 Builder 模式运用,再往里深入就和 RequestCreator 没太大关系了。后面再展开分析内部的各个奥妙点。
网友评论