步骤
get():
双锁创建Picasso
单例,通过Builder
模式创建
Picasso singleton;
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;
}
private Context;
public Builder(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("Context must not be null.");
}
// 这里得到的是ApplicationContext的上下文
this.context = context.getApplicationContext();
}
可以看到传入的只是Builder到这里只有一个上下文context
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);
}
调用build的时候,我们会创建多个东西:
-
downloader
: OKHTTP的下载器 -
LruCache
: 缓存器 -
PicassoExecutorService
: 线程池服务 -
transformer
: 变压器
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<>(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<>();
this.targetToDeferredRequestCreator = new WeakHashMap<>();
this.indicatorsEnabled = indicatorsEnabled;
this.loggingEnabled = loggingEnabled;
this.referenceQueue = new ReferenceQueue<>();
this.cleanupThread = new CleanupThread(referenceQueue, HANDLER);
this.cleanupThread.start();
}
这里我们才真正真正初始化了单例Picasso ,最后传入对应的配置。接下开看看比较重要的:
allRequestHandlers
:是一个容器内部包裹了多个RequestHandler
,用来解决不同的请求,举个例子,家里装了新家
我们需要做的事情就是请工人安装,但是工人分为好几种,电工.管道.工装空调的等等他们各司其职 ,用来解决不同的场景,而这里我们就可以把他看成一个一个的“工人”。
--
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);
}
这里可以看见调用load的时候我们进行判断过滤,输入的满足条件就可以开始创建RequestCreator
类
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);
}
做了两点,传入picasso
引用,创建Request.Builder
,这里load就结束了其实,我们发现返回的其实是包装好了的RequestCreator
,而且RequestCreator
内部包含引用data=Request.Builder
,但是到目前为止我们什么都还没做,只是初始化了相关配置,创建了RequestCreator而已。
into
好戏现在才开始
public void into(ImageView target, Callback callback) {
long started = System.nanoTime();
checkMain();
if (target == null) {
throw new IllegalArgumentException("Target must not be null.");
}
if (!data.hasImage()) {
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);
}
private Request createRequest(long started) {
int id = nextId.getAndIncrement();
Request request = data.build();
request.id = id;
request.started = started;
boolean loggingEnabled = picasso.loggingEnabled;
if (loggingEnabled) {
log(OWNER_MAIN, VERB_CREATED, request.plainId(), request.toString());
}
Request transformed = picasso.transformRequest(request);
if (transformed != request) {
// If the request was changed, copy over the id and timestamp from the original.
transformed.id = id;
transformed.started = started;
if (loggingEnabled) {
log(OWNER_MAIN, VERB_CHANGED, transformed.logId(), "into " + transformed);
}
}
return transformed;
}
boolean hasImage() {
return uri != null || resourceId != 0;
}
- 我们检查是不是在主线程
- 判断
uri
或者resourceId
,即来源是不是为空,为空就直接取消请求,设置占位图 - 是不是调用
fit()
,但是不能和resize
一起用 - 创建
Request
实体,即Request.Builder.build(),这里有过滤然后设置该Request
为普通 - 设置
resourceId
还有开始的时间 -
createRequest(started)
设置了transformed转换器的一个接口,实现该接口应该可以手动改变request,如果转换后的request
变更我们就改变他 - 是不是需要在缓存中找,如果找到取消请求,设置图片,回调成功接口。
- 没有找到就设置占位图,创建一个
Action
,调用picasso.enqueueAndSubmit(action);
Action 和 Requst的对比。
如果查看区别的话,其实属性都可以查看出来:
我们看看Request
属性:
int id;
long started;
int networkPolicy;
public final Uri uri;
public final int resourceId;
public final String stableKey;
public final List<Transformation> transformations;
public final int targetWidth;
public final int targetHeight;
public final boolean centerCrop;
public final boolean centerInside;
public final boolean onlyScaleDown;
public final float rotationDegrees;
public final float rotationPivotX;
public final float rotationPivotY;
public final boolean hasRotationPivot;
public final Bitmap.Config config;
public final Priority priority;
再来看看Action
属性
final Picasso picasso;
final Request request;
final WeakReference<T> target;
final boolean noFade;
final int memoryPolicy;
final int networkPolicy;
final int errorResId;
final Drawable errorDrawable;
final String key;
final Object tag;
boolean willReplay;
boolean cancelled;
我们可以看见Request
跟注重的是他请求的本身 如:id,请求的来源等等 ,而Action
更倾向于一个加载的任务而且它持有Request
引用,还有任务的状态,各种策略等,所以是包含关系。
回到代码中:
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);
}
void submit(Action action) {
dispatcher.dispatchSubmit(action);
}
因为Target
是所谓的虚引用,然后拿出Target
判断是不是和数组里的Target
一样不一样就更新。最后交给了
dispatcher.dispatchSubmit(action)
。
Dispatcher
中文译为分发者即 : 任务分发者
- 他是在
Picassio
初始化的时候被创建的是 - 每一个
Dispatcher
都关联了 线程池 下载器 缓存 监控器 以及主线程的Handler
- 他是发动机,也是我们的控制或者说中心
-
Dispatcher
内部有一个东西即DispatcherHandler
这是Dispatcher
自己的Handler
,他绑定的是Dispatcher
子线程的Looper
Dispatcher. dispatchSubmit():
void dispatchSubmit(Action action) {
handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
}
而这个handler
就是我们之前聊到的DispatcherHandler
,相当于把发送的任务交给了子线程,我看看接下来到底是怎样的吧:
这里插一句 : 子线程handleMessage
调用performSubmit
方法不多讲中间过程不多讲
oid performSubmit(Action action, boolean dismissFailed) {
//注意哦,这里已经不在主线程了,而是在dispatcher线程(HandlerThread)
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
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());
}
}
- 判断任务是不是被暂停了,如果暂停了就放入暂停的容器里
- 在通过
key
在huntermap
中寻找有没有对应的hunter
- 判断是不是线程被关闭,不然就
return
- 创建对应的
Hunter
,等一会儿详细讨论它 - 调用直接把
Hunter
扔进去线程池中,为什么能直接扔,看下面 - 然后把
Hunter
和key
为键值对放到hunterMap
容器中
BitmapHunter
中文翻译 图像猎人 脑洞真大
先看实现接口吧:
class BitmapHunter implements Runnable
实现了Runable的接口,这说明了上文的为什么可以直接扔进线程池的原因,所以等等我们思路就跟更加明了直接在run
方法看线程池执行的过程
不过在这之前我们得看看Hunter
是如何创建的:
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);
}
你还记得之前创建的千万个RequestHandler
吗?现在我们正调用canHandleRequest
方法,一个一个对比他们的能力是不是和这个任务匹配,如果找到就创建BitmapHunter
对象并且传入对应requestHandler
引用。
接下来,emm.....看Hunter
的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
“打猎” ,然后传回打猎的结果 - 如果有结果 调用
dispatcher.dispatchComplete(this)
否则调用dispatcher.dispatchFailed(this);
然后看看怎么“打的猎吧”
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;
}
}
//缓存没有命中的话,再调用requestHandler.load
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
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;
}
- 缓存里面找
- 没找到调用
Handler
的load
方法了 -
load
拿到结果取出bitmap
- 查看我们是不是要变化即
data.needsTransformation
,如果要变化就变化 - 最后返回
bitmap
给Hunter
,调用dispatcher.dispatchComplete(this)
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");
}
}
- 首先根据缓存策略看是不是缓存
- 执行完后应该删除hunter在容器中
- 批量处理
hunter
然后看看怎么批量处理的吧:
batch = new ArrayList();
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_DELAY
是200ms 意思说在这期间全部返回的结果都交给了batch
这个容器,最后延迟200ms发送信息
事情又回到了dispatcher
的子线程中,而子线程会调用dispatcher.performComplete(hunter);
void performBatchComplete() {
List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
batch.clear();
mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
logBatch(copy);
}
- 子线程提出批量的
BitmapHunter
然后清空batch
- 最后交给主线程批量更新UI
下面就看看主线程吧:
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);
}
不用说逐个调用hunter.picasso.complete(hunter);
@Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
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, 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);
}
}
那我们再开看看分发deliverAction
方法吧
private void deliverAction(Bitmap result, LoadedFrom from, Action action, Exception e) {
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(e);
if (loggingEnabled) {
log(OWNER_MAIN, VERB_ERRORED, action.request.logId(), e.getMessage());
}
}
}
这里没什么要讲得只是解决前面的判断问题吗,继续轮到ImageViewAction. complete
@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));
}
//得到target也就是ImageView
ImageView target = this.target.get();
if (target == null) {
return;
}
Context context = picasso.context;
boolean indicatorsEnabled = picasso.indicatorsEnabled;
//通过PicassoDrawable来将bitmap设置到ImageView上
PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
//回调callback接口
if (callback != null) {
callback.onSuccess();
}
}
- 判断
target
很简单,因为他是弱引用,有个能target
到达这里已经被回收了 -
PicassoDrawable.setBitmap
方法吧bitmap
设置给ImageView
- 调用成功的回调
特别说一下PicassoDrawable
负责入场动画,但是这里不深入
到这里所以的流程都结束了
我们需要简化总接一下流程即:
创建->入队->执行->解码->变换->批处理->完成->分发->显示(可选)
- 创建:get()初始化配置,创建
picassio
,调用load()
创建RequestCreater
- 入队:在
into
中,RequestCreater
创建request
,通过request
创建一个Action
即 一个完整的任务,交给picasso
最后传给dispatcher
的子线程,子线程找到对应的ReqestHandler
,并打包Action
传给Hunter
,最后把Hunter
放入线程池入队 - 解码: 在线程池中,调用
ReqestHandler
的load
方法尝试解码Action
- 变换: 拿到解码后的结果后判断是不是需要变换,如果需要就进行变化
- 批处理 :将变换后的结果放入容器
batch
中,每隔0.2秒线程池就发送给dispatcher
的子线程信息,子线程接收到信息后取出batch
容器内容,并把它发送给主线程,主线程接受到对象逐个处理容器中每个hunter
返回的结果 - 完成: 上面就会调用
picasso.complete(hunter)方法:
代表任务执行完成 - 分发:
complete
判断是不是需要分发如果需要就调用deliverAction(result, from, join);
方法 - 显示 : 上面任务就会调用
action.complete(result, from);
根据action
的不同调用对饮的显示方法
设计模式
建造者模式
建造者模式是指:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式应该是我们都比较熟悉的一种模式,在创建AlertDialog的时候通过配置不同的参数就可以展示不同的AlertDialog,这也正是建造者模式的用途,通过不同的参数配置或者不同的执行顺序来构建不同的对象,在Picasso里当构建RequestCreator的时候正是使用了这种设计模式,我们通过可选的配置比如centerInside(),placeholder()等等来分别达到不同的使用方式,在这种使用场景下使用建造者模式是非常合适的.
责任链模式
责任链模式是指:一个请求沿着一条“链”传递,直到该“链”上的某个处理者处理它为止。当我们有一个请求可以被多个处理者处理或处理者未明确指定时。可以选择使用责任链模式,在Picasso里当我们通过BitmapHunter的forRequest()方法构建一个BitmapHunter对象时,需要为Request指定一个对应的RequestHandler来进行处理Request.这里就使用了责任链模式,依次调用requestHandler.canHandleRequest(request)方法来判断是否该RequestHandler能处理该Request如果可以就构建一个RequestHandler对象返回.这里就是典型的责任链思想的体现。
引用:Picasso源代码分析