Picasso从使用到源码解析
一、使用
Picasso是Square公司开源的一个Android图形缓存库,地址http://square.github.io/picasso/,可以实现图片下载和缓存功能。仅仅只需要一行代码就能完全实现图片的异步加载:
Picasso.with(context).load(url).into(imageView);
Picasso不仅实现了图片异步加载的功能,还解决了Android中加载图片时需要解决的一些常见问题:
- 在adapter中需要取消已经不在视野范围的ImageView图片资源的加载,否则会导致图片错位,Picasso已经解决了这个问题。
- 使用复杂的图片压缩转换来尽可能的减少内存消耗
- 自带内存和硬盘二级缓存功能
同时它还支持以下功能:
1. 图片转换:转换图片以适应布局大小并减少内存占用
Picasso.with(context).load(url).resize(50, 50).centerCrop().into(imageView);
这里的50单位是px,使用dp则换成下面这句,并定义相应资源文件。
resizeDimen(R.dimen.iv_width,R.dimen.iv_height)
2. 自定义转换:
public class CustomTransformation implements Transformation {
@Override public Bitmap transform(Bitmap source) {
Bitmap result;
//一些处理
if (result != source) {
source.recycle();//一定要recycle!!!!
}
return result;
}
@Override public String key() { return "key"; }
}
//使用
Picasso.with(context).load(url).transform(new CustomTransformation()).into(imageView);
3. 空白或者错误占位图片: picasso提供了两种占位图片,未加载完成或者加载发生错误的时需要一张图片作为提示。
Picasso.with(context)
.load(url)
.placeholder(R.drawable.placeholder)
.error(R.drawable.placeholder_error)
.into(imageView);
4. 资源文件的加载:除了加载网络图片picasso还支持加载Resources, assets, files, content providers中的资源文件。
Picasso.with(context).load(R.drawable.***).into(imageView1);
Picasso.with(context).load(new File(...)).into(imageView2);
二、解析
1.with(context)方法
//Picasso.java
public static Picasso with(Context context) {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
singleton = new Builder(context).build();
}
}
}
return singleton;
}
可以看出这是一个DCL(双重检查锁)实现的单例模式,其中单例的实例化又是通过Builder模式实现。接下来看下Builder中做了哪些工作。
//Picasso.java
public static class Builder {
private final Context context;
private Downloader downloader;//下载器
private ExecutorService service;//线程池
private Cache cache;//缓存方式
private Listener listener;//Linstener为一接口,只有onImageLoadFailed(...)方法
private RequestTransformer transformer;//变换器
private List<RequestHandler> requestHandlers;
private Bitmap.Config defaultBitmapConfig;
private boolean indicatorsEnabled;
private boolean loggingEnabled;
/** Start building a new {@link Picasso} instance. */
public Builder(Context context) {
if (context == null) {
throw new IllegalArgumentException("Context must not be null.");
}
this.context = context.getApplicationContext();
}
...
public Picasso build() {
Context context = this.context;
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context);
}
if (cache == null) {
cache = new LruCache(context);//cache默认LruCache
}
if (service == null) {
service = new PicassoExecutorService();
}
if (transformer == null) {
transformer = RequestTransformer.IDENTITY;//默认不转换
}
Stats stats = new Stats(cache);
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
}
...
}
Builder中主要对Picasso的参数进行了初始化工作,下边看下其中Downloader的初始化:
//Utils.java
static Downloader createDefaultDownloader(Context context) {
try {
Class.forName("com.squareup.okhttp.OkHttpClient");
return OkHttpLoaderCreator.create(context);
} catch (ClassNotFoundException ignored) {
}
return new UrlConnectionDownloader(context);
}
可以看出Downloader会通过反射判断是否能够使用OkHttp,不能则使用HttpURLConnection,不过这里的包名是OkHttp3以前的写法,现在我们都是使用OkHttp3了,而OkHttp3的包名是okhttp3.OkHttpClient,所以即使你在项目中引用了OkHttp3,Picasso还是会把HttpUrlConnection当作下载器来下载图片的,这个问题估计Picasso会在以后的版本中修正吧,接着看下service的初始化:
//PicassoExecutorService.java 继承ThreadPoolExecutor
PicassoExecutorService() {
super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
}
service其实就是一个线程池,其中DEFAULT_THREAD_COUNT=3,所以这里构造了一个仅仅只有3个核心线程并使用优先级队列调度的线程池。接下来的就是Picasso的构造方法了:
//Picasso.java
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;
int builtInHandlers = 7; // Adjust this as intern