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 internal handlers are added or removed.
int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
List<RequestHandler> allRequestHandlers =
new ArrayList<RequestHandler>(builtInHandlers + extraCount);
// ResourceRequestHandler needs to be the first in the list to avoid
// forcing other RequestHandlers to perform null checks on request.uri
// to cover the (request.resourceId != 0) case.
allRequestHandlers.add(new ResourceRequestHandler(context));
if (extraRequestHandlers != null) {
allRequestHandlers.addAll(extraRequestHandlers);
}
allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
allRequestHandlers.add(new MediaStoreRequestHandler(context));
allRequestHandlers.add(new ContentStreamRequestHandler(context));
allRequestHandlers.add(new AssetRequestHandler(context));
allRequestHandlers.add(new FileRequestHandler(context));
allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
requestHandlers = Collections.unmodifiableList(allRequestHandlers);
this.stats = stats;
this.targetToAction = new WeakHashMap<Object, Action>();
this.targetToDeferredRequestCreator = new WeakHashMap<ImageView, DeferredRequestCreator>();
this.indicatorsEnabled = indicatorsEnabled;
this.loggingEnabled = loggingEnabled;
this.referenceQueue = new ReferenceQueue<Object>();
this.cleanupThread = new CleanupThread(referenceQueue, HANDLER);
this.cleanupThread.start();
}
在Picasso的构造方法里主要进行了一些变量的初始化,也初始化了RequestHandler,初始化RequestHandler时首先将我们提交进来的requestHandler加入到集合中,然后还往allRequestHandlers中提交了其它的RequestHandler,这些不同的RequestHandler,分别用来处理不同的资源,比如加载相册的图片、加载资产文件夹中的图片、加载网络图片等。
2、load(…)方法
public RequestCreator load(String path) {
if (path == null) {
return new RequestCreator(this, null, 0);
}
if (path.trim().length() == 0) {
throw new IllegalArgumentException("Path must not be empty.");
}
return load(Uri.parse(path));
}
这里不管使用url,file,ResourceID都会构造一个RequestCreator对象:
//Picasso.java
RequestCreator(Picasso picasso, Uri uri, int resourceId) {
if (picasso.shutdown) {
throw new IllegalStateException(
"Picasso instance already shut down. Cannot submit new requests.");
}
this.picasso = picasso;
this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
}
这里shutdown属性是判断Picasso实例是否已经停止运行,如果已经shutdown则抛异常,否则将我们即将要加载的图片信息保存在data中,data是一个Request.Builder对象,其中保存了一些图片加载信息:宽高、是否centerCrop、转换器、优先级、Config等等。接下的调用的一些配置方法就是修改data中的值了:
//Picasso.java
public RequestCreator resize(int targetWidth, int targetHeight) {
data.resize(targetWidth, targetHeight);
return this;
}
public RequestCreator centerCrop() {
data.centerCrop();
return this;
}
public RequestCreator transform(Transformation transformation) {
data.transform(transformation);
return this;
}
3、into(imageview)方法
//RequestCreator.java
public void into(ImageView target) {
into(target, null);
}
public void into(ImageView target, Callback callback) {//Callback为接口 只有onSucess,onError方法
long started = System.nanoTime();
checkMain();//检查是否主线程
if (target == null) {
throw new IllegalArgumentException("Target must not be null.");
}
if (!data.hasImage()) {//hasImage:uri != null || resourceId != 0
picasso.cancelRequest(target);
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
return;
}
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);
}
Request request = createRequest(started);
String requestKey = createKey(request);
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 =
new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
errorDrawable, requestKey, tag, callback, noFade);
picasso.enqueueAndSubmit(action);
}
into方法首先会检查是否在主线程,不在则抛出异常,然后检查data中是否有uri或者resourceId,没有则取消请求并判断是否有占位图片,有则用来占位,然后返回。否则会判断deferred,它的设置是使用.fit()方法设置:
public RequestCreator fit() {
deferred = true;
return this;
}
/** Internal use only. Used by {@link DeferredRequestCreator}. */
RequestCreator unfit() {
deferred = false;
return this;
}
这里fit表示图片在加载的过程中能够自由缩放以填满整个ImageView,这里如果设置成了fit,会判断是否设置宽高,如果设置有宽高则与fit相冲突,会抛出异常。接下来系统来获取ImageView的宽和高,如果ImageView的宽和高为0的话,则首先把占位图片设置上。
这时候就要开始请求了,这里会创建一个Request对象,接着会调用shouldReadFromMemoryCache(…),判断是否该从内存中读取图片,如果是,则根据key从Cache中读取一张图片出来,能够取出图片则取消请求,设置图片,回调onSucess() 返回。
接着会创建Action,并且将Action添加到一个Picasso的enqueueAndSubmit方法中。接下来我们就来看看这个请求入队的方法:
//Picasso.java
void enqueueAndSubmit(Action action) {
Object target = action.getTarget();
if (target != null && targetToAction.get(target) != action) {
// This will also check we are on the main thread.
cancelExistingRequest(target);
targetToAction.put(target, action);
}
submit(action);
}
首先获取action里边的target,其实就是ImageView,如果这个ImageView不为空,并且该ImageView已经有了一个Action,则取消已经存在的请求,然后重新给该target设置Action,完了之后就是submit了,我们来看看这个submit:
//Picasso.java
void submit(Action action) {
dispatcher.dispatchSubmit(action);
}
dispatcher,就是在build方法中实例化的,这里调用了dispatcher的dispatchSubmit方法,点击去再看:
//Dispatcher.java
void dispatchSubmit(Action action) {
handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
}
这里的Handler:
//Dispatcher.java
private static class DispatcherHandler extends Handler {
private final Dispatcher dispatcher;
public DispatcherHandler(Looper looper, Dispatcher dispatcher) {
super(looper);
this.dispatcher = dispatcher;
}
@Override public void handleMessage(final Message msg) {
switch (msg.what) {
case REQUEST_SUBMIT: {
Action action = (Action) msg.obj;
dispatcher.performSubmit(action);
break;
}
case REQUEST_CANCEL: {
Action action = (Action) msg.obj;
dispatcher.performCancel(action);
break;
}
case TAG_PAUSE: {
Object tag = msg.obj;
dispatcher.performPauseTag(tag);
break;
}
case TAG_RESUME: {
Object tag = msg.obj;
dispatcher.performResumeTag(tag);
break;
}
case HUNTER_COMPLETE: {
BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performComplete(hunter);
break;
}
case HUNTER_RETRY: {
BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performRetry(hunter);
break;
}
case HUNTER_DECODE_FAILED: {
BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performError(hunter, false);
break;
}
case HUNTER_DELAY_NEXT_BATCH: {
dispatcher.performBatchComplete();
break;
}
case NETWORK_STATE_CHANGE: {
NetworkInfo info = (NetworkInfo) msg.obj;
dispatcher.performNetworkStateChange(info);
break;
}
case AIRPLANE_MODE_CHANGE: {
dispatcher.performAirplaneModeChange(msg.arg1 == AIRPLANE_MODE_ON);
break;
}
default:
Picasso.HANDLER.post(new Runnable() {
@Override public void run() {
throw new AssertionError("Unknown handler message received: " + msg.what);
}
});
}
}
}
所以这里调用的是dispatcher.performSubmit(action);
//Dispatcher.java
void performSubmit(Action action) {
performSubmit(action, true);
}
void performSubmit(Action action, boolean dismissFailed) {
if (pausedTags.contains(action.getTag())) {
pausedActions.put(action.getTarget(), action);
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),
"because tag '" + action.getTag() + "' is paused");
}
return;
}
BitmapHunter hunter = hunterMap.get(action.getKey());
if (hunter != null) {
hunter.attach(action);
return;
}
if (service.isShutdown()) {
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
}
return;
}
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
hunter.future = service.submit(hunter);
hunterMap.put(action.getKey(), hunter);
if (dismissFailed) {
failedActions.remove(action.getTarget());
}
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
}
}
首先判断该请求是否该暂停,接下来关键的是调用forRequest方法给hunter赋值,我们来看看这个forRequest方法:
//BitmapHunter.java
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
Action action) {
Request request = action.getRequest();
List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
// Index-based loop to avoid allocating an iterator.
//noinspection ForLoopReplaceableByForEach
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);
}
这里有一个for循环,for循环中的东西就是我们所有的RequestHandler,然后通过一个if来匹配,看使用那个RequestHandler来处理我们的图片加载。
接着将hunter(实现Runnable接口)加入到线程池中,所以现在要看的就是BitmapHnnter中的run方法了:
@Override public void run() {
try {
updateThreadName(data);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
}
result = hunt();
if (result == null) {
dispatcher.dispatchFailed(this);
} else {
dispatcher.dispatchComplete(this);
}
} catch (Downloader.ResponseException e) {
if (!e.localCacheOnly || e.responseCode != 504) {
exception = e;
}
dispatcher.dispatchFailed(this);
} catch (NetworkRequestHandler.ContentLengthException e) {
exception = e;
dispatcher.dispatchRetry(this);
} catch (IOException e) {
exception = e;
dispatcher.dispatchRetry(this);
} catch (OutOfMemoryError e) {
StringWriter writer = new StringWriter();
stats.createSnapshot().dump(new PrintWriter(writer));
exception = new RuntimeException(writer.toString(), e);
dispatcher.dispatchFailed(this);
} catch (Exception e) {
exception = e;
dispatcher.dispatchFailed(this);
} finally {
Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);
}
}
这里首先更新线程名,然后通过hunt()方法获取Bitmap,根据Bitmap是否为null调用不同的方法,最终通过在dispatcher中的Handler发送Message到主线程。最后看下这个hunt()是怎么获取图片的:
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
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;
}
}
data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
RequestHandler.Result result = requestHandler.load(data, networkPolicy);
if (result != null) {
loadedFrom = result.getLoadedFrom();
exifRotation = result.getExifOrientation();
bitmap = result.getBitmap();
// If there was no Bitmap then we need to decode it from the stream.
if (bitmap == null) {
InputStream is = result.getStream();
try {
bitmap = decodeStream(is, data);
} finally {
Utils.closeQuietly(is);
}
}
}
if (bitmap != null) {
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId());
}
stats.dispatchBitmapDecoded(bitmap);
if (data.needsTransformation() || exifRotation != 0) {
synchronized (DECODE_LOCK) {
if (data.needsMatrixTransform() || exifRotation != 0) {
bitmap = transformResult(data, bitmap, exifRotation);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
}
}
if (data.hasCustomTransformations()) {
bitmap = applyCustomTransformations(data.transformations, bitmap);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");
}
}
}
if (bitmap != null) {
stats.dispatchBitmapTransformed(bitmap);
}
}
}
return bitmap;
}
首先是判断是否可以从内存中获取这张图片,如果可以,将图片加载出来并返回,并更新stats中相关变量,否则就会requestHandler中读取,这里的requestHandler是在BitmapHunter中forRequest方法匹配出来的RequestHander,这里下载网络图片,匹配的就是NetworkRequestHandler,那我们看看NetworkRequestHandler里边的load方法:
//NetworkRequestHandler.java
@Override public Result load(Request request, int networkPolicy) throws IOException {
Response response = downloader.load(request.uri, request.networkPolicy);
if (response == null) {
return null;
}
Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;
Bitmap bitmap = response.getBitmap();
if (bitmap != null) {
return new Result(bitmap, loadedFrom);
}
InputStream is = response.getInputStream();
if (is == null) {
return null;
}
// Sometimes response content length is zero when requests are being replayed. Haven't found
// root cause to this but retrying the request seems safe to do so.
if (loadedFrom == DISK && response.getContentLength() == 0) {
Utils.closeQuietly(is);
throw new ContentLengthException("Received response with 0 content-length header.");
}
if (loadedFrom == NETWORK && response.getContentLength() > 0) {
stats.dispatchDownloadFinished(response.getContentLength());
}
return new Result(is, loadedFrom);
}
这里其实就是调用downloader的load方法获取Response,然后从response得到Bitmap返回,downloader的load方法就是熟悉的网络请求代码,就不去看了。到这里,整个请求过程都结束了。接下的就是显示和缓存图片啦。
请求成功与失败在run()中分别会调用dispatcher的dispatchComplete(this)和dispatchFailed(this):
//Dispatcher.java
void dispatchComplete(BitmapHunter hunter) {
handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
}
void dispatchFailed(BitmapHunter hunter) {
handler.sendMessage(handler.obtainMessage(HUNTER_DECODE_FAILED, hunter));
}
发送Message后又分别会调用dispatcher.performComplete(hunter)和 dispatcher.performError(hunter, false):]
//Dispatcher.java
void performComplete(BitmapHunter hunter) {
if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
cache.set(hunter.getKey(), hunter.getResult());
}
hunterMap.remove(hunter.getKey());
batch(hunter);
if (hunter.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
}
}
void performError(BitmapHunter hunter, boolean willReplay) {
if (hunter.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter),
"for error" + (willReplay ? " (will replay)" : ""));
}
hunterMap.remove(hunter.getKey());
batch(hunter);
}
这里如果请求成功的话就判断是否存入内存缓存,是则缓存,接着batch:
//Dispatcher.java
private void batch(BitmapHunter hunter) {
if (hunter.isCancelled()) {
return;
}
batch.add(hunter);
if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
}
}
首先判断hunter是否已经被取消,是则直接返回,否则将hunter加入到batch中,然后判断Handler中是否有一条HUNTER_DELAY_NEXT_BATCH消息,没有的话就发一条,接着会调用performBatchComplete():
//Dispatcher.java
void performBatchComplete() {
List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
batch.clear();
mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
logBatch(copy);
}
然后mainThreadHandler又发送一条消息,这个mainThreadHandler是在build方法中我们创建Dispatch实例的时候传入的Handler,是在主线程中创建的Handler,在Picasso类里,我们找到了HUNTER_BATCH_COMPLETE这个case:
//Picasso.java
case HUNTER_BATCH_COMPLETE: {
@SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, n = batch.size(); i < n; i++) {
BitmapHunter hunter = batch.get(i);
hunter.picasso.complete(hunter);
}
break;
}
//Picasso.java
void complete(BitmapHunter hunter) {
Action single = hunter.getAction();
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);
}
if (hasMultiple) {
//noinspection ForLoopReplaceableByForEach
for (int i = 0, n = joined.size(); i < n; i++) {
Action join = joined.get(i);
deliverAction(result, from, join);
}
}
if (listener != null && exception != null) {
listener.onImageLoadFailed(this, uri, exception);
}
}
在这里,我们拿到Bitmap,然后去派发Action,如果有合并的Action则在25行进行派发,我们来看看这个派发操作:
private void deliverAction(Bitmap result, LoadedFrom from, Action action) {
if (action.isCancelled()) {
return;
}
if (!action.willReplay()) {
targetToAction.remove(action.getTarget());
}
if (result != null) {
if (from == null) {
throw new AssertionError("LoadedFrom cannot be null.");
}
action.complete(result, from);
if (loggingEnabled) {
log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + from);
}
} else {
action.error();
if (loggingEnabled) {
log(OWNER_MAIN, VERB_ERRORED, action.request.logId());
}
}
}
这里如果Bitmap不为空,调用action的complete方法,为空则调用action得error方法,这里action得创建是在into方法调用得时候传入的,是个ImageViewAction对象,接着看它的这两个方法:
//ImageViewAction.java
@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();
}
}
@Override public void error() {
ImageView target = this.target.get();
if (target == null) {
return;
}
if (errorResId != 0) {
target.setImageResource(errorResId);
} else if (errorDrawable != null) {
target.setImageDrawable(errorDrawable);
}
if (callback != null) {
callback.onError();
}
}
//PicassoDrawable.java
static void setBitmap(ImageView target, Context context, Bitmap bitmap,
Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
Drawable placeholder = target.getDrawable();
if (placeholder instanceof AnimationDrawable) {
((AnimationDrawable) placeholder).stop();
}
PicassoDrawable drawable =
new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
target.setImageDrawable(drawable);
}
到这里就是成功则通过PicassoDrawable设置图片,失败则直接设置失败占位图,这样整个加载图片的过程就结束啦~