Picasso是Github上的一款很受欢迎的图片加载框架,它可以从网络、assert、本地文件等资源处加载图片,并提供了图片缓存功能。
Picasso的简单使用如下所示,将网络上的一个图片加载到ImageView中。
Picasso.get().load(url).fit().into(imageView)
Picasso加载图片的过程大致如下:
1、构建图片加载任务;
1.1、获取Picasso单实例。在调用Picasso的get方法时会获取一个Picasso单实例。
1.2 、构建图片加载任务。在调用Picasso的load(url)方法时,会构建一个RequestCreater,以接收对图片进行的各种处理,并以此构建一个图片加载请求(Request)。
1.3、提交图片加载任务。在调用into(ImageView)方法时,会根据RequestCreater配置的信息,实例化一个图片加载请求Request,并将此请求封装到一个图片加载任务(Action)中,最后,将其提交给图片处理器Dispatcher。
2、执行图片加载任务
2.1、将图片处理任务放到线程池中。图片处理器Dispatcher中有一个线程池ThreadPoolExecutor变量,在Dispatcher接收到一个图片加载任务(Action)时,会根据任务的类型获取一个对应类型的RequestHandler(用于加载图片),并将其和任务(Action)一起封装到BitmapHunter中。最后,将BitmapHunter加入线程池。任务的类型由图片的来源决定,可以从资源、Assert或网络等地方获取图片。
2.2、进行图片的加载及处理。在BitmapHunter继承了Runnable,其加入到线程池后,会被调度执行,去加载指定图片。加载图片的大体流程为:BitmapHunter会先去cache中检查是否有已经加载好的图片,若有,则将图片返回;若没有,则使用RequestHandler加载图片。在图片加载完成后,会根据Request中对图片的要求,对图片进行变化处理。在处理完成之后,会将包含Bitmap的BitmapHunter发送给Dispachter。
2.3、保存处理好的图片。在Dispachter收到一个包含Bitmap的BitmapHunter时,其首先会将Bitmap保存到cache。然后,将BitmapHunter添加到batch链表中,此链表包含了所有已经加载到Bitmap的BitmapHunter。最后,使用HANDLER发送一个延迟消息,通知Piscasso去处理加载到的图片。延迟的目的是为了收集一批已经加载到图片,进行集中处理,以提高处理效率(此处的处理在主线程执行)。
3、处理得到的图片
3.1、处理图片。在HANDLER接收到图片处理通知后,会遍历batch中的BitmapHunter,并将BitmapHunter交给Picasso处理。Picasso则会提取出BitmapHunter中的Bitmap对象,然后交给Action去处理,比如给ImageView设置图片。
Picasso整体架构如下所示:
Picasso各个部分解释及作用:
Request | 保存了图片的URL或资源ID,包含了需要对图片进行的设置,如宽高、需要旋转的角度等 |
Action | 保存了放置图片的目标(如ImageView)、Request、存储cache策略等,可以看作是一个图片加载的任务 |
RequestHandler | 真正加载图片的类,内部有一个load方法,输入参数为Request;输出结果为包含图片的Result。Picasso中存在多个类型的RequestHandler,用以从资源、Assert和网络等地方加载图片 |
BitmapHunter | 这是一个Runnable类,主要作用为1、从cache或使用RequestHandler加载图片;2、在图片加载后,按照Request中的要求对图片进行变换;3、在图片变换完成后,将图片发送给DispatcherHandler,让其执行接下来的处理步骤。 |
Dispatcher | 管理图片加载任务,包含启动图片加载,暂停图片加载,取消图片加载等操作。 |
DispatcherHandler | 执行在子线程,处理收到的图片加载、暂停、取消等消息。根据得到的消息,将具体任务交给Dispatcher处理。 |
HANDLER | 执行在主线程。1、用来处理已经加载到图片,如将图片设置给ImageView。2、回收加载任务(Action)。3、重新执行加载任务。 |
Picasso | 提交图片加载任务,并在获取图片后对其进行处理 |
Picasso加载图片的代码流程如下:
1、构建图片加载任务
1.1、获取Picasso单实例
在调用Picasso调用get方法时,会获取一个Picasso单实例,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;
}
可以看到这里采用双重检查机制构建了一个Picasso单实例,PicassoProvider继承了CotentProvider,其context在ContentProvider创建时获取。这里通过Builder的build方法来构建一个Piscasso单实例,build方法如下:
public Picasso build() {
Context context = this.context;
if (downloader == null) {
downloader = new OkHttp3Downloader(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);
}
在这里会对如下类进行实例化,OkHttp3Downloader、LruCache、PicassoExecutorService、RequestTransformer。然后将其封装到Dispatcher中,Dispatcher负责图片加载任务的调度。最后,将以上对象封装到Picasso中。在此先介绍一下这几个类的作用
OKHttp3Downloader代码如下,其作用是使用OkHttp3网络库执行网络请求,一般来说,此网络请求是加载图片的网络请求。
public final class OkHttp3Downloader implements Downloader {
@VisibleForTesting final Call.Factory client;
private final Cache cache;//OkHttp3使用的cache
private boolean sharedClient = true;
......
@NonNull @Override public Response load(@NonNull Request request) throws IOException {
//使用OkHttp3进行网络请求
return client.newCall(request).execute();
}
@Override public void shutdown() {
if (!sharedClient && cache != null) {
try {
cache.close();
} catch (IOException ignored) {
}
}
}
}
LruCache的代码如下,其用来存储加载过的图片,它其实只是对Android中的LruCache进行了封装。
public final class LruCache implements Cache {
final android.util.LruCache<String, LruCache.BitmapAndSize> cache;
......
@Nullable @Override public Bitmap get(@NonNull String key) {
BitmapAndSize bitmapAndSize = cache.get(key);
return bitmapAndSize != null ? bitmapAndSize.bitmap : null;
}
@Override public void set(@NonNull String key, @NonNull Bitmap bitmap) {
if (key == null || bitmap == null) {
throw new NullPointerException("key == null || bitmap == null");
}
int byteCount = Utils.getBitmapBytes(bitmap);
// If the bitmap is too big for the cache, don't even attempt to store it. Doing so will cause
// the cache to be cleared. Instead just evict an existing element with the same key if it
// exists.
if (byteCount > maxSize()) {
cache.remove(key);
return;
}
cache.put(key, new BitmapAndSize(bitmap, byteCount));
}
......
}
PicassoExecutorService的代码如下,它继承了ThreadPoolExecutor,其本质为一个线程池,默认配置为核心线程数为3,最大线程数为3。此配置由网络情况决定,当网络情况好时,其线程数就更多一些,例如,在WIFI情况下,其核心线程数和最大线程数为4。
class PicassoExecutorService extends ThreadPoolExecutor {
private static final int DEFAULT_THREAD_COUNT = 3;
PicassoExecutorService() {
super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
}
void adjustThreadCount(NetworkInfo info) {
if (info == null || !info.isConnectedOrConnecting()) {
setThreadCount(DEFAULT_THREAD_COUNT);
return;
}
switch (info.getType()) {
case ConnectivityManager.TYPE_WIFI:
case ConnectivityManager.TYPE_WIMAX:
case ConnectivityManager.TYPE_ETHERNET:
setThreadCount(4);
break;
case ConnectivityManager.TYPE_MOBILE:
......
}
}
@Override
public Future<?> submit(Runnable task) {
PicassoFutureTask ftask = new PicassoFutureTask((BitmapHunter) task);
execute(ftask);
return ftask;
}
}
RequestTransformer代码如下,其会对图片请求进行修改
public interface RequestTransformer {
/**
* Transform a request before it is submitted to be processed.
*
* @return The original request or a new request to replace it. Must not be null.
*/
Request transformRequest(Request request);
/** A {@link RequestTransformer} which returns the original request. */
RequestTransformer IDENTITY = new RequestTransformer() {
@Override public Request transformRequest(Request request) {
return request;
}
};
}
1.2、构建图片加载任务
在得到Picasso实例后,我们会调用Picasso的load方法,并给出要加载图片的来源(url网络或本地资源文件)。load方法的代码如下:
public RequestCreator load(@Nullable 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));
}
public RequestCreator load(@Nullable Uri uri) {
return new RequestCreator(this, uri, 0);
}
可以看到在此处会构造一个RequestCreator对象,用来接收图片变换请求。RequestCreator采用了构造者模式,来构造图片加载请求,其内部包含了一个Request.Builder对象,用来构建Request。比如我们可以调用RequestCreator的fit方法,来告诉Picasso我们需要使图片填充满ImageView。RequestCreator的fit代码如下:
public class RequestCreator {
private static final AtomicInteger nextId = new AtomicInteger();
private final Picasso picasso;
private final Request.Builder data;//用来接收图片变换请求,并构造图片请求
......
public RequestCreator fit() {
deferred = true;
return this;
}
......
}
1.3、提交图片加载请求
在我们设置完对图片的要求后,可以调用into方法,让Picasso去异步加载图片,并在图片加载结束后,将图片设置我们指定的ImageView。into方法的代码如下:
public void into(ImageView target) {
into(target, null);
}
public void into(ImageView target, Callback callback) {
long started = System.nanoTime();
//检查是否在主线程执行,要在主线程执行此方法
checkMain();
......
//检查是否有设置url或资源ID,若没有则返回
if (!data.hasImage()) {
picasso.cancelRequest(target);
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
return;
}
//调用fit方法后,deferred会被设置为true,在此处需要设置图片的目标宽高,即ImageView的宽高
//以使图片填充满ImageView
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());
}
//当前ImageView可以还未被测量,添加一个View绘制监听,以在View绘制时,再次设置图片的
//目标宽度和高度
picasso.defer(target, new DeferredRequestCreator(this, target, callback));
return;
}
//ImageView已经被测量过了,将ImageView的宽高设置为图片的宽高
data.resize(width, height);
}
//根据之前设置图片变换要求,创建一个图片请求
Request request = createRequest(started);
//根据图片请求创建一个唯一的标识字符串,可以依据此值从cache中提取图片
String requestKey = createKey(request);
if (shouldReadFromMemoryCache(memoryPolicy)) {
//从cache中提取图片
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
if (bitmap != null) {
//若从cache中得到了图片,则不需要执行加载请求,并可以马上给对应的target(ImageView)
//设置图片
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;
}
}
......
//创建一个图片加载任务
Action action =
new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
errorDrawable, requestKey, tag, callback, noFade);
//提交图片加载任务
picasso.enqueueAndSubmit(action);
}
在此可以看到,首先会根据之前设置图片格式要求,构建一个request请求。然后将此request封装到一个图片请求任务ImageViewAction中。最后,调用Picasso的enqueueAndSubmit方法提交图片加载请求。enqueueAndSubmit方法的代码如下:
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);
//保存要放置图片的target与图片加载任务Action之间的映射关系
targetToAction.put(target, action);
}
submit(action);
}
void submit(Action action) {
dispatcher.dispatchSubmit(action);
}
在此,可以看到enqueueAndSubmit方法会调用dispatcher的dispatchSubmit方法,此方法最终会将图片加载任务提交到一个专门处理图片加载任务的子线程。dispatchSubmit方法代码如下:
void dispatchSubmit(Action action) {
handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
}
此处的handler为DispatcherHandler,此DispatcherHandler与DispatcherThread相关联,DispatcherThread是一个子线程,在此子线程中,会对图片加载任务进行管理,包含启动图片加载,暂停图片加载等管理操作。在此,handler会将图片加载任务action封装到一个类型为REQUEST_SUBMIT的消息中,并将其发送到DispatcherThread线程。
2、执行图片加载任务
2.1、将图片加载任务加入线程池
当DispatcherThread中的Looper接收到REQUEST_SUBMIT类型的消息后,其会交给DispatcherHandler去处理,代码如下:
@Override public void handleMessage(final Message msg) {
switch (msg.what) {
case REQUEST_SUBMIT: {
//获取消息中的图片加载任务
Action action = (Action) msg.obj;
dispatcher.performSubmit(action);
break;
}
......
}
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;
}
......
//将图片加载任务放到一个Runnable(hunter)中
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
//将hunter加入线程池,去加载图片
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方法创建一个BitmapHunter对象,进而使用此对象去加载对应的图片。forRequest方法的代码如下:
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
Action action) {
Request request = action.getRequest();
List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
//遍历Picasso中的RequestHandler,并检查RequestHandler是否能够加载request中要求的图片
for (int i = 0, count = requestHandlers.size(); i < count; i++) {
RequestHandler requestHandler = requestHandlers.get(i);
if (requestHandler.canHandleRequest(request)) {
//找到了可以加载Request中图片的RequestHandler,将其封装到BitmapHunter中
return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
}
}
return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
}
在这里先介绍一下Picasso中的RequestHandler,RequestHandler是一个抽象类,用来加载图片。在Picasso中,会根据图片的来源选择相应的RequestHandler去加载图片。RequestHandler的子类主要有以下几种:
Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats,
Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {
......
// 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.
//根据资源ID加载图片
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));
//加载Assert中的图片
allRequestHandlers.add(new AssetRequestHandler(context));
//加载本地文件中的图片
allRequestHandlers.add(new FileRequestHandler(context));
//加载网络中的图片
allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
requestHandlers = Collections.unmodifiableList(allRequestHandlers);
......
}
在选择RequestHandler时,会调用canHandlerRequest来检查是否可以使用当前的RequestHandler来加载图片,以NetWorkRequestHandler为例,展示canHandlerRequest方法:
@Override public boolean canHandleRequest(Request data) {
String scheme = data.uri.getScheme();
return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme));
}
可以看出,如果我们之前设置了从网络中加载图片,并给了一个图片的URL,那么此方法就会返回true,表示NetWorkRequestHandler可以处理这种的图片加载请求。
最后,将得到BitmapHunter加入到线程池中,去进行图片的加载操作。
2.2、图片的加载及处理
图片是在BitmapHunter中加载的,此类是一个Runnable类型的类,其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 (NetworkRequestHandler.ResponseException e) {
if (!NetworkPolicy.isOfflineOnly(e.networkPolicy) || e.code != 504) {
exception = e;
}
dispatcher.dispatchFailed(this);
}
......
}
在此方法中,先会hunt方法去加载一个图片,然后,使用Dispatcher发送一个消息告知图片已经加载完成。hunt方法的代码如下:
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
//先检查cache中是否有加载过的图片
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();
//若ReqeustHandler并没有直接加载出图片,而是得到了图片的数据流,
//则将数据流解码为图片
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) {
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId());
}
stats.dispatchBitmapDecoded(bitmap);
//对加载到的图片进行变换处理
if (data.needsTransformation() || exifOrientation != 0) {
synchronized (DECODE_LOCK) {
if (data.needsMatrixTransform() || exifOrientation != 0) {
bitmap = transformResult(data, bitmap, exifOrientation);
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");
}
}
}
......
}
}
return bitmap;
}
2.3、保存处理好的图片
图片加载处理完成之后,会调用dispatchComplete方法,告知图片加载完成,dispatchComplete方法代码如下:
void dispatchComplete(BitmapHunter hunter) {
handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
}
对应的消息处理方法代码如下:
void performComplete(BitmapHunter hunter) {
//保存图片到cahce
if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
cache.set(hunter.getKey(), hunter.getResult());
}
hunterMap.remove(hunter.getKey());
//将加载到图片的BitmapHunter添加到链表
batch(hunter);
if (hunter.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
}
}
private void batch(BitmapHunter hunter) {
if (hunter.isCancelled()) {
return;
}
if (hunter.result != null) {
hunter.result.prepareToDraw();
}
batch.add(hunter);
//发送一个延时消息,通知Picasso的主线程可以展示图片了
//延时:为了获取一批加载完成的图片,提供处理效率
if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
}
}
此处的handler为HANDLER,其与主线程相关联。
3、处理得到的图片
接上一节,子线程在图片加载完成后,会提交一个延时消息,此消息的处理方法为:
static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case HUNTER_BATCH_COMPLETE: {
//处理加载好的图片,BitmapHunter中包含了加载好的图片
for (int i = 0, n = batch.size(); i < n; i++) {
BitmapHunter hunter = batch.get(i);
hunter.picasso.complete(hunter);
}
break;
}
......
default:
throw new AssertionError("Unknown handler message received: " + msg.what);
}
}
};
此方法会调用Picasso的comleter方法,代码如下:
void complete(BitmapHunter hunter) {
Action single = hunter.getAction();
//一系列的图片加载任务(Action),拥有相同的key值
List<Action> joined = hunter.getActions();
......
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);
}
//处理key值相同的图片加载任务
if (hasMultiple) {
//noinspection ForLoopReplaceableByForEach
for (int i = 0, n = joined.size(); i < n; i++) {
Action join = joined.get(i);
deliverAction(result, from, join, exception);
}
}
......
}
可以看到在此方法中,会调用deliverAction去处理加载到图片,其代码如下:
private void deliverAction(Bitmap result, LoadedFrom from, Action action, Exception e) {
......
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(e);
if (loggingEnabled) {
log(OWNER_MAIN, VERB_ERRORED, action.request.logId(), e.getMessage());
}
}
}
会调用图片加载任务Action中的complete方法以处理图片,以ImageViewAction为例:
@Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
......
//获取目标ImageView
ImageView target = this.target.get();
if (target == null) {
return;
}
Context context = picasso.context;
boolean indicatorsEnabled = picasso.indicatorsEnabled;
//给ImageView 设置图片
PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
if (callback != null) {
callback.onSuccess();
}
}
至此,Picasso的图片加载流程已经结束。