建议使用builder创建Picasso
Picasso.Builder builder = new Picasso.Builder(this);
Picasso.setSingletonInstance(builder.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();
}
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);
}
通过构造方法得知有几个参数值得关注:
downloader、cache、service、transformer
通过反射判断使用OkHttpLoaderCreator或者UrlConnectionDownloader,通过OkHttpLoaderCreator建立的downloader还会通过getCacheDir方法获取缓存路径建立文件名为“picasso-cache”的Picasso的缓存文件。其中OkHttpDownloader为通过OkHttp请求来下载网络图片的类,具体内容体现在后面讲述的load方法中。
缓存默认使用LRU算法,即least-recently used,近期最少使用算法。首先在LruCache的构造函数中计算出一个合理的大小(15%、1/7),作为缓存的最大空间。
PicassoExecutorService实现Picasso线程池,构造函数中实例化工作队列和线程工厂。
RequestTransformer,请求在被执行前经过一层转换。
通过Stat标记缓存的状态(命中数、未命中数、总大小、平均大小、下载次数等)
分发处理事件,如图片加载完成、请求提交、请求取消等。
public RequestCreator load(Picasso picasso, Uri uri, int resourceId) {
if (picasso.shutdown) {
throw new IllegalStateException(
"Picasso instance already shut down. Cannot submit new requests.");
}
this.picasso = picasso;
//消息都存在RequestCreator.data中,最终返回对象类型为RequestCreator
this.data = new Request.Builder(uri, resourceId,
picasso.defaultBitmapConfig);
}
最终返回对象类型为RequestCreator,可以理解为一个构造器,我们可以在后面添加占位符等,这些参数会暂存在RequestCreator的成员变量内部,等到用户调用into(imageview)方法时,再把所有参数填充到Request
public void into(ImageView target, Callback callback) {
long started = System.nanoTime();
checkMain(); //确保在主线程调用该方法
if (target == null) {
throw new IllegalArgumentException("Target must not be null.");
}
//如果图片的uri为null,则取消请求,然后设置占位图
if (!data.hasImage()) {
picasso.cancelRequest(target);
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
return;
}
//如果调用了RequestCreator#fit()方法,那么deferred会被设置为true
//这是因为fit()方法需要适应ImageView的大小,必须等到ImageView的layout过程完毕才能fit()
//因此,这里实际上是推迟了图片的加载过程,即Picasso#defer()
if (deferred) {
if (data.hasSize()) {
throw new IllegalStateException("Fit cannot be used with resize.");
}
int width = target.getWidth();
int height = target.getHeight();
if (width == 0 || height == 0) {
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
picasso.defer(target, new DeferredRequestCreator(this, target, callback));
return;
}
data.resize(width, height);
}
这里切开分析,前面这里进行了几个确认:是否在主线程、是否设置了targetview,是否设置了uri、是否同时设置了fix和resize,是否设置了占位符,fix限制targetView为ImagerView,其内部会完成测量和裁剪在这里插入代码片
Request request = createRequest(started); //根据RequestCreator的参数来创建一个Request
String requestKey = createKey(request); //创建与该Request对应的一个Key
//如果内存缓存可用,那么直接从内存缓存获取Request对应的Bitmap,并取消请求
if (shouldReadFromMemoryCache(memoryPolicy)) {
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
if (bitmap != null) {
picasso.cancelRequest(target);
setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
if (picasso.loggingEnabled) {
log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
}
if (callback != null) {
callback.onSuccess();
}
return;
}
}
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
//Action封装了图片请求的系列信息
Action action = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy,errorResId,errorDrawable, requestKey, tag, callback, noFade);
picasso.enqueueAndSubmit(action); //排队,等待调度
}
这里主要是创建Request,装入RequestBuilder的成员变量信息
先检查内存缓存是否可用,如果可用就直接加载,并取消request
若不可用才创建action进入队伍
void dispatchSubmit(Action action) {
handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
}
向dispatcher所在线程发送消息
//Dispatcher.DispatcherHandler#handleMessage
private static class DispatcherHandler extends Handler {
//...
@Override
public void handleMessage(final Message msg) {
switch (msg.what) {
case REQUEST_SUBMIT: {
Action action = (Action) msg.obj;
dispatcher.performSubmit(action);
break;
}
//...
}
}
//代码清单4-1:Dispatcher#performSubmit(action)
void performSubmit(Action action, boolean dismissFailed) {
//省略...
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
hunter.future = service.submit(hunter);
hunterMap.put(action.getKey(), hunter);
}
//BitmapHunter#forRequest
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
Action action) {
Request request = action.getRequest();
List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
//找到一个可以处理该Request的RequestHandler
for (int i = 0, count = requestHandlers.size(); i < count; i++) {
RequestHandler requestHandler = requestHandlers.get(i);
if (requestHandler.canHandleRequest(request)) {
return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
}
}
return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
}
从上面的代码可以看出,先是生成了一个BitmapHunter,这个类的作用顾名思义,就是获取Bitmap,它是一个Runnable,它内部根据Request的不同类型来确定不同的获取方法(实际上是RequestHandler在起作用)。
紧接着,调用了service.submit(hunter)方法,这里的service实际上就是PicassoExecutorService线程池,将BitmapHunter这个runnable投递进了线程池,如果线程池有空闲的线程那么就会执行这个runnable,否则阻塞等待。最终,如果runnable获得执行的机会,它的run()方法会被调用。
//代码清单5:BitmapHunter#run
@Override public void run() {
try {
updateThreadName(data);
//获取result
result = hunt();
if (result == null) {
//如果加载失败,则分发失败事件
dispatcher.dispatchFailed(this);
} else {
//如果加载成功,则分发成功事件
dispatcher.dispatchComplete(this);
}
}
//省略异常状态的处理...
}
//代码清单5-1:BitmapHunter#hunt()
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
//从内存缓存读取bitmap,如果命中则添加计数
if (shouldReadFromMemoryCache(memoryPolicy)) {
bitmap = cache.get(key);
if (bitmap != null) {
stats.dispatchCacheHit();
loadedFrom = MEMORY;
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
}
return bitmap;
}
}
查询缓存是否可以获取,否则进行网络解析
networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
//利用requestHandler解析图片请求
RequestHandler.Result result = requestHandler.load(data, networkPolicy);
if (result != null) {
loadedFrom = result.getLoadedFrom();
exifOrientation = result.getExifOrientation();
bitmap = result.getBitmap();
// If there was no Bitmap then we need to decode it from the stream.
// 如果bitmap为空,那么从stream读取字节流解析成bitmap
if (bitmap == null) {
Source source = result.getSource();
try {
bitmap = decodeStream(source, data);
} finally {
try {
//noinspection ConstantConditions If bitmap is null then source is guranteed non-null.
source.close();
} catch (IOException ignored) {
}
}
}
}
if (bitmap != null) {
//省略部分代码...
//对Bitmap进行转换操作,Transformation是一个自定义的转换操作
if (data.needsTransformation() || exifOrientation != 0) {
synchronized (DECODE_LOCK) {
if (data.needsMatrixTransform() || exifOrientation != 0) {
bitmap = transformResult(data, bitmap, exifOrientation);
}
if (data.hasCustomTransformations()) {
bitmap = applyCustomTransformations(data.transformations, bitmap);
}
}
}
}
return bitmap;
}
此处就是进行转换,先前设置的参数作用的地方,以及自己设置的transformation
此方法返回bitmap,并调用dispatcher.dispatchComplete(this),从线程池里面的线程切换为dispatcher线程
void performComplete(BitmapHunter hunter) {
//如果条件允许,那么把该bitmap缓存到内存
if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
cache.set(hunter.getKey(), hunter.getResult());
}
hunterMap.remove(hunter.getKey()); //从map移除这个已完成的hunter
batch(hunter); //进行批处理
}
private void batch(BitmapHunter hunter) {
if (hunter.isCancelled()) {
return;
}
if (hunter.result != null) {
hunter.result.prepareToDraw();
}
batch.add(hunter); //添加到batch列表内
if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
}
}
从上面源码可以看出,该hunter会被添加到一个batch的列表内,同时延迟发送一个HUNTER_DELAY_NEXT_BATCH消息,这意味着,第一个hunter完成后,会被添加到batch列表,然后延迟200ms发送batch消息。此时如果有别的hunter到达,也会被一一添加到batch列表,直到一开始的batch消息得到处理。这里利用了批处理的思想,在200ms的等待时间内,会暂存多个hunter请求,时间到了之后便切换到主线程进行UI的显示,这样就不用频繁地进行线程切换,可以提升UI显示的流畅性。
void performBatchComplete() {
List<BitmapHunter> copy = new ArrayList<>(batch);
batch.clear();
mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
logBatch(copy);
}
这里的mainThreadHandler是持有主线程Looper的handler,它发送的消息都会在主线程得到处理,在dispatcher初始化传入了主线程的looper
//代码清单8-1:Picasso#complete
void complete(BitmapHunter hunter) {
//获取hunter所含有的Action
Action single = hunter.getAction();
//hunter可能对应多个Action,对同一图片的同一操作的多个请求会保存在一个hunter内
//避免不必要的重复加载步骤。
List<Action> joined = hunter.getActions();
boolean hasMultiple = joined != null && !joined.isEmpty();
boolean shouldDeliver = single != null || hasMultiple;
if (!shouldDeliver) {
return;
}
Uri uri = hunter.getData().uri;
Exception exception = hunter.getException();
Bitmap result = hunter.getResult();
LoadedFrom from = hunter.getLoadedFrom();
if (single != null) {
deliverAction(result, from, single, exception);
}
if (hasMultiple) {
//noinspection ForLoopReplaceableByForEach
for (int i = 0, n = joined.size(); i < n; i++) {
Action join = joined.get(i);
deliverAction(result, from, join, exception);
}
}
if (listener != null && exception != null) {
listener.onImageLoadFailed(this, uri, exception);
}
}
取出action,调用deliverAction
//代码清单8-2:Picasso#deliverAction
private void deliverAction(Bitmap result, LoadedFrom from, Action action, Exception e) {
//省略...
if (result != null) {
action.complete(result, from);
} else {
action.error(e);
}
@Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
if (result == null) {
throw new AssertionError(
String.format("Attempted to complete action with no result!\n%s", this));
}
ImageView target = this.target.get();
if (target == null) {
return;
}
Context context = picasso.context;
boolean indicatorsEnabled = picasso.indicatorsEnabled;
PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
if (callback != null) {
callback.onSuccess();
}
}
PicassoDrawable继承自BitmapDrawable,Picasso是以Drawable的形式把图片设置进ImageView的,通过这样的形式,Picasso可以最后在图片上添加一些信息。比如,开启了Debug模式后,所加载的图片的右下角会有不同颜色的角标来表示图片的来源(网络、内存或磁盘),这个功能的实现就是借助于BitmapDrawable.draw方法在画布上添加额外的信息。
最后调用callback.onSuccess
static void setBitmap(ImageView target, Context context, Bitmap bitmap,
Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
Drawable placeholder = target.getDrawable();
if (placeholder instanceof Animatable) {
((Animatable) placeholder).stop();
}
PicassoDrawable drawable =
new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
target.setImageDrawable(drawable); //最后的最后,把drawable设置进imageview上
}