Picasso是Squareup公司出的一款图片加载框架,能够解决我们在开发中加载图片时遇到的许多问题,其中最重要的是解决了加载大量图片时出现卡顿的问题,提高了加载速度,使图片能够平滑的加载。
1、基本应用
Picasso.with(this)
//加载网络图片
.load(url)
//对图片进行剪裁,200表示200px
.resize(200,200)
//与resize一起使用,用于图片缩放
.centerCrop()//完全填满ImageView,多余部分被裁掉(CenterInside使图片完全显示,但可能无法填满ImageView)
//fit不可以和resize一起使用,缩小图片的尺寸去适配ImageView
//.fit()
//在transform方法中对图片进行二次处理
.transform(transformation)
//占位图,图片加载出来之前显示的默认图片
.placeholder(R.mipmap.ic_launcher)
//错误图,图片加载出错时显示的图片
.error(R.mipmap.ic_launcher)
//设置缓存策略
//第一个参数是指图片加载时放弃在内存缓存中查找
//第二个参数是指图片加载完不缓存在内存中
.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
//在iv中show
.into(iv);
//在加载过程中设置监听器
.into(iv, new Callback() {
@Override
public void onSuccess() {
}
@Override
public void onError() {
}
});
Transformation transformation = new Transformation() {
@Override
public Bitmap transform(Bitmap source) {
//对图片进行二次处理
}
return blankBitmap;
}
};
2、源码浅析
先从Picasso.with(this)看起
public static Picasso with(Context context) {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
singleton = new Builder(context).build();
}
}
}
return singleton;
}
这是一个单例,在创建Picasso的过程中,调用了new Builder(context).build()方法,说明Picasso实例创建的代码在build方法中,那就来看看这个build方法
public Picasso build() {
Context context = this.context;
//初始化下载器
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context);
}
//初始化缓存
if (cache == null) {
cache = new LruCache(context);
}
//初始化线程池
if (service == null) {
service = new PicassoExecutorService();
}
//Request转换器,用于在提交任务之前做一些处理,默认不做处理
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);
}
先看第一个条件判断
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context);
}
如果使用with方法downloader肯定为null,则系统会创建一个默认的downloader
static Downloader createDefaultDownloader(Context context) {
try {
Class.forName("com.squareup.okhttp.OkHttpClient");
return OkHttpLoaderCreator.create(context);
} catch (ClassNotFoundException ignored) {
}
return new UrlConnectionDownloader(context);
}
系统通过反射来检查项目中是否使用了OkHttp,如果使用了,就使用OkHttp来创建一个下载器,否则就使用HttpUrlConnection来创建,Class.forName("com.squareup.okhttp.OkHttpClient");这个方法的参数,这是OkHttp3以前的写法,现在使用的OkHttp3的包名就不是这个,而是okhttp3.OkHttpClient,所以如果项目中引用的是OkHttp3,Picasso还是会把HttpUrlConnection当作下载器来下载图片的。
第二个条件判断
if (cache == null) {
cache = new LruCache(context);
}
实例化LruCache,其内部使用的是LinkedHashMap
public LruCache(Context context) {
this(Utils.calculateMemoryCacheSize(context));
}
public LruCache(int maxSize) {
if(maxSize <= 0) {
throw new IllegalArgumentException("Max size must be positive.");
} else {
this.maxSize = maxSize;
//LinkedHashMap是HashMap的一个子类,
//它保留插入的顺序,输出的顺序和输入时的相同
this.map = new LinkedHashMap(0, 0.75F, true);
}
}
第三个条件判断
if (service == null) {
service = new PicassoExecutorService();
}
class PicassoExecutorService extends ThreadPoolExecutor {
private static final int DEFAULT_THREAD_COUNT = 3;
PicassoExecutorService() {
super(3, 3, 0L, TimeUnit.MILLISECONDS, new PriorityBlockingQueue(), new PicassoThreadFactory());
}
....
}
PicassoExecutorService继承自ThreadPoolExecutor线程池,线程池中的核心线程数为3,线程池的最大线程数也为3,说明线程池中没有非核心线程,线程队列使用了优先级队列PriorityBlockingQueue(使用方法:.priority(Picasso.Priority.HIGH/MEDIUM/LOW))
public Future> submit(Runnable task) {
PicassoExecutorService.PicassoFutureTask ftask = new PicassoExecutorService.PicassoFutureTask((BitmapHunter)task);
this.execute(ftask);
return ftask;
}
可以看到在submit时,new PicassoFutureTask
private static final class PicassoFutureTask extends FutureTask implements Comparable {
private final BitmapHunter hunter;
public PicassoFutureTask(BitmapHunter hunter) {
super(hunter, (Object)null);
this.hunter = hunter;
}
public int compareTo(PicassoExecutorService.PicassoFutureTask other) {
Priority p1 = this.hunter.getPriority();
Priority p2 = other.hunter.getPriority();
//高优先级的请求放到队列前面
//同等级的请求按照顺序先后执行
return p1 == p2?this.hunter.sequence - other.hunter.sequence:p2.ordinal() - p1.ordinal();
}
}
可以看到PicassoFutureTask是一个实现了Comparable接口的FutureTask
另外在PicassoExecutorService这个类中还有一个方法:void adjustThreadCount(NetworkInfo info),对移动网络做了处理,在做网络类型判断时可以参考