Picasso 图片加载总体流程

我将整个请求加载过程分为 3 步。
第一步:创建请求。涉及到 RequestCreator, ImageViewAction, Dispatcher 等。
第二步:线程池执行请求。涉及到 PicassoExecutorService, PicassoFutureTask, BitmapHunter, RequestHandler, OkHttp3Downloader, Dispatcher 等。
第三步:拿到请求结果,对结果的处理和展示。BitmapHunter, Dispatcher, Picasso, PicassoDrawable, ImageViewAction。
第一步,创建请求
public void into(ImageView target, Callback callback) {
//省略部分代码......
Action action = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId, errorDrawable, requestKey, tag, callback, noFade);
picasso.enqueueAndSubmit(action);
}
从 into 方法调用说起,创建了一个 ImageViewAction 类型的对象,关键入参有 target, request, requestKey 等,这个 action 将作为关键数据结构被传递到后面流程。
void enqueueAndSubmit(Action action) {
Object target = action.getTarget();
if (target != null && targetToAction.get(target) != action) {
cancelExistingRequest(target);
targetToAction.put(target, action);
}
submit(action);
}
通过 Picasso 对象提交这个 action。根据 target (也就是 ImageView 对象)先检查下有没有别的 Action 动作,有的话要做一次清退,然后提交这个新的 action。
void submit(Action action) {
dispatcher.dispatchSubmit(action);
}
提交 Action 的动作实际交给了 Dispatcher 对象,这点和 OkHttp 类似。
接下去请注意,这里要切换线程了。
void dispatchSubmit(Action action) {
handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
}
Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler, Downloader downloader, Cache cache, Stats stats) {
//省略部分代码......
this.dispatcherThread = new DispatcherThread();
this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
}
Dispatcher 类里有个静态类 DispatcherThread 继承自 HandlerThread,还有一个静态类 DispatcherHandler 继承自 Handler,再看看 Dispatcher 构造方法。嗯~ 原来是这样。
当 Dispatcher 提交 Action 时,利用这个 Handler 对象给这个子线程的消息队列里发了一个 REQUEST_SUBMIT 消息,这么一来就把工作切到子线程去处理了。
@Override public void handleMessage(final Message msg) {
switch (msg.what) {
case REQUEST_SUBMIT: {
Action action = (Action) msg.obj;
dispatcher.performSubmit(action);
break;
}
}
}
Action 对象始终在传递,接下去真的要提交了。
void performSubmit(Action action, boolean dismissFailed) {
//省略部分代码......
BitmapHunter hunter = hunterMap.get(action.getKey());
if (hunter != null) {
hunter.attach(action);
return;
}
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
hunter.future = service.submit(hunter);
hunterMap.put(action.getKey(), hunter);
}
创建关键对象 BitmapHunter 对象,并将 hunter 交给 service 进行提交。这个 service 实际就是 PicassoExecutorService,还记得前面说过的这个线程池吗?hunter 是个 Runnable 类型,所以可以作为入参。另外还要注意,前面传递过来的 action,被用来创建 hunter 对象了,所以接下去 hunter 变成了请求数据对象的载体。
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action) {
Request request = action.getRequest();
List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
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 对象的创建,会根据是否有 RequestHandler 能处理来进行创建。根据前面的分析,假设我们想要展示的是一张网络图片,那么这里满足条件的将是 NetworkRequestHandler 。
接下去又要注意了,我们又要切线程了,并且即将进入第二步。
第二步,线程池执行请求
我们说线程池执行请求,实际上是通过线程池分配线程来完成 Runnable 的任务,也就是在线程里执行 BitmapHunter 的 run 方法。
@Override public void run() {
result = hunt();
if (result == null) {
dispatcher.dispatchFailed(this);
} else {
dispatcher.dispatchComplete(this);
}
}
通过 hunt 方法获取到 Bitmap 类型的结果值 result,再通过 Dispatcher 对象分发完成还是失败。
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
if (shouldReadFromMemoryCache(memoryPolicy)) {
bitmap = cache.get(key);
if (bitmap != null) {
stats.dispatchCacheHit();
loadedFrom = MEMORY;
return bitmap;
}
}
RequestHandler.Result result = requestHandler.load(data, networkPolicy);
if (result != null) {
bitmap = result.getBitmap();
if (bitmap == null) {
Source source = result.getSource();
bitmap = decodeStream(source, data);
}
}
if (bitmap != null) {
stats.dispatchBitmapDecoded(bitmap);
if (data.needsTransformation() || exifOrientation != 0) {
//省略
}
}
return bitmap;
}
根据内存缓存策略还是优先会从缓存里获取,如果取到就不再继续,直接返回结果。如果没有,这时就要通过 requestHandler 对象来处理了。假设这里是 NetworkRequestHandler 类型,那么实际就是一个网络请求,并将请求结果包装成 Result 类型返回。
包装成的 Result 对象里其实并没有 Bitmap 类型的值,需要将网络数据做一次解码,即 decodeStream 操作,这样 bitmap 就有值了。
如果此时图片需要做变换,那就下去就会对 bitmap 做形变之类的操作,这不是我们的重点,就省略了。
对于返回的 Bitmap 类型结果,完成和失败的处理类似,我们以完成为例,进行第三步的分析。
第三步,拿到请求结果,对结果的处理和展示
void dispatchComplete(BitmapHunter hunter) {
handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
}
又回到了 Dispatcher 进行消息的发送,显然这里又要切线程了,接下去的工作将交给 DispatcherThread 线程处理。
@Override public void handleMessage(final Message msg) {
switch (msg.what) {
case HUNTER_COMPLETE: {
BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performComplete(hunter);
break;
}
}
}
传过来的数据对象是 hunter,这样来说可以最大限度的引用到一些相关的东西。
void performComplete(BitmapHunter hunter) {
if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
cache.set(hunter.getKey(), hunter.getResult());
}
hunterMap.remove(hunter.getKey());
batch(hunter);
}
如果需要缓存,这里就会把图片缓存到内存里,缓存的机制就是前面说的 LruCache。接着从 hunterMap 里移除,还记得前面 performSubmit 的时候会把这个 hunter 给加进去。最后调用 batch 方法,我的理解是,可能图片请求有好多个,毕竟用了线程池,同时可以有 3 个请求,这样一来有可能他们几乎同时完成,那么在展示上就让他们作为一批进行处理。
private void batch(BitmapHunter hunter) {
if (hunter.isCancelled()) {
return;
}
if (hunter.result != null) {
hunter.result.prepareToDraw();
}
batch.add(hunter);
if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
}
}
batch 是个 BitmapHunter 类型的 List 集合,hunter 被添加之后,如果没有 HUNTER_DELAY_NEXT_BATCH 这个延时消息,就发送一个,这里的延时是 200ms,也就是说间隔 200ms 处理一次图片展示。就跟公交车一样,定时发车,有可能车上只有 1 个乘客,有可能很多个,但这里在没有 hunter 时是不会发车的,而公交车有可能空车也发。
这里采用发消息的形式,我觉得主要是利用延时的功能,而并非线程切换。
void performBatchComplete() {
List<BitmapHunter> copy = new ArrayList<>(batch);
batch.clear();
mainThreadHandler.sendMessage(
mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
}
这里才是线程切换,将要展示的 hunters 通过消息传递给主线程,因为想要做 UI 相关的操作,必须在主线程。从名字上可以看出这个 Handler 是个主线程的 Handler,或者从它的赋值来源也可以知道,就是 Picasso 类里的 HANDLER 变量。
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case HUNTER_BATCH_COMPLETE: {
List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
for (int i = 0, n = batch.size(); i < n; i++) {
BitmapHunter hunter = batch.get(i);
hunter.picasso.complete(hunter);
}
break;
}
}
}
还是调用了 Picasso 对象的 complete 方法,这里可能会有疑问,为什么要通过 hunter 来引用 picasso 对象,原因是这个 HANDLER 是用 static final 修饰的,因此没办法持有外部类引用,自然就不能直接调用 complete 方法。
void complete(BitmapHunter hunter) {
//省略部分代码......
Action single = hunter.getAction();
Bitmap result = hunter.getResult();
LoadedFrom from = hunter.getLoadedFrom();
if (single != null) {
deliverAction(result, from, single, exception);
}
if (listener != null && exception != null) {
listener.onImageLoadFailed(this, uri, exception);
}
}
我们就看单个 action 的,hunter 还能获取多个 action,这个先不管。获取到 single 这个 Action 后就会去分发它。
private void deliverAction(Bitmap result, LoadedFrom from, Action action, Exception e) {
if (result != null) {
action.complete(result, from);
} else {
action.error(e);
}
}
前面说到这个 action 是 ImageViewAction 类型,那么要处理的就是 complete 或者 error 操作,前面大致看过 error 方法,这里再看下 complete 方法里的代码。
static void setBitmap(ImageView target, Context context, Bitmap bitmap, Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
PicassoDrawable drawable = new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
target.setImageDrawable(drawable);
}
PicassoDrawable 继承自 BitmapDrawable,这个没研究过,不太懂,应该就是可以将 Bitmap 对象作为参数生成 Drawable 类型对象吧。最后调用 ImageView 的 setImageDrawable 方法完成图片展示。
网友评论