前言
大家好,我是小黑(linheimx),一位快乐的2b程序员。
一段简洁的代码
Picasso
.with(context)
.load(url)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.fit()
.tag(context)
.into(view);
这段代码将 异步的 加载url指定的资源到 imageview 上。
接下来,我们将以这段代码作为 切入点 ,来分析Picasso
1. with()
public static Picasso with(@NonNull Context context) {
// 1.check
if (context == null) {
throw new IllegalArgumentException("context == null");
}
// 2. sync code
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
// 构造
singleton = new Builder(context).build();
}
}
}
return singleton;
}
这段代码是为了获取 Picasso 实例。
有以下关键点:
- synchronized
当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
- builder
builder构造模式,建造Picasso所需要的信息。
细看Builder
builder 可以构建的信息有:
public static class Builder {
private final Context context;
private Downloader downloader;
private ExecutorService service;
private Cache cache;
private Listener listener;
private RequestTransformer transformer;
private List<RequestHandler> requestHandlers;
private Bitmap.Config defaultBitmapConfig;
private boolean indicatorsEnabled;
private boolean loggingEnabled;
build() 方法
/** Create the {@link Picasso} instance. */
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 downloader.
load:从外部存储,缓存或是网络来加载图片资源。
- Cache cache
内存缓存:存储最近使用最多的图片。
- ExecutorService service
异步线程并发执行器
以上3个小东西,构造了 Dispatcher。说明这个 Dispatcher 比较重要。(暂不分析)
2. load()
load 指定加载资源路径
1. load(@Nullable String path) // 可以是 url,file资源(file:),content资源(content:),android资源(resource:)
2. load(@NonNull File file) // image file
3. load(@DrawableRes int resourceId) // drawable resource ID
最终执行如下方法:
public RequestCreator load(@Nullable Uri uri) {
return new RequestCreator(this, uri, 0);
}
我们可以看到 load的最终目的是,构造一个 RequestCreator
RequestCreator 从名字可以看出 它 可以构建Request,它的一切字段,方法都是为Request来服务的。
我们大致看一下 RequestCreator:
- 字段:
// 静态原子 int
private static final AtomicInteger nextId = new AtomicInteger();
private final Picasso picasso;
private final Request.Builder data;//重要!!!
private boolean noFade;
private boolean deferred;// 推迟,延期
private boolean setPlaceholder = true;
private int placeholderResId;
private int errorResId;
private Drawable placeholderDrawable;
private Drawable errorDrawable;
private int memoryPolicy;
private int networkPolicy;
private Object tag;
- 方法
字段是私有的,很多方法是为字段服务的。在方法里面可做相关的非法检查等。
noPlaceholder()
placeholder(@DrawableRes int placeholderResId)
placeholder(@NonNull Drawable placeholderDrawable)
error(@DrawableRes int errorResId)
error(@NonNull Drawable errorDrawable)
tag(@NonNull Object tag)
fit() // Attempt to resize the image to fit exactly into the target {@link ImageView}'s bounds. This will result in delayed execution of the request until the {@link ImageView} has been laid out.
unfit()
resizeDimen(int targetWidthResId, int targetHeightResId)
RequestCreator resize(int targetWidth, int targetHeight) // Resize the image to the specified size in pixels.
centerCrop()
centerInside()
等等。。。。。
注意:这里很多方法 都有return this--->便于链式的方法调用(写出来的代码方便直观!)
注意:这些方法很多都是为request服务的(Request 需要构建,Request.Builder data提供构造信息)
3. into()
RequestCreator 中很重要的方法!!!
into有几个重载:
- into(@NonNull Target target)
- into( @NonNull RemoteViews remoteViews, @IdRes int viewId, int notificationId, @NonNull Notification notification)
- into(ImageView target)
- into(ImageView target, Callback callback)
下面主要分析: into(ImageView target, Callback callback)
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 || target.isLayoutRequested()) {
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);
}
简单的画了下流程图,如下:
简单直接的说几个关键点:
a. 从内存缓存中读取数据
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;
}
}
- shouldReadFromMemoryCache() 此处方法
检查 图片的内存策略
被定义在 enum MemoryPolicy中,枚举类 MemoryPolicy负责定义,负责do some job,比较有意思。
- Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
Picasso 是个大管家。持有cache,方便对内存中的bitmap进行管理。
- picasso.cancelRequest(target);
Picasso 是个大管家。还可以cancel请求呢。
- setbitmap,calback
有了bitmap,就可以显示啦。
setbitmap()这个方法很有意思,后面会聊它的(// todo analysis)
b. 再深入一点(亚麻跌):
这个Picasso对 tagert的请求管理。
picasso.cancelRequest(target): 取消与target对象相关的所有请求
public void cancelRequest(@NonNull ImageView view) {
// checkMain() is called from cancelExistingRequest()
if (view == null) {
throw new IllegalArgumentException("view cannot be null.");
}
cancelExistingRequest(view);// go
}
private void cancelExistingRequest(Object target) {
checkMain();
Action action = targetToAction.remove(target);// 注意:targetToAction
if (action != null) {
/////////////////////// true cancel //////////////////////
action.cancel();
dispatcher.dispatchCancel(action);
/////////////////////// true cancel //////////////////////
}
if (target instanceof ImageView) {
ImageView targetImageView = (ImageView) target;
//////////////////////////// defer cancel ///////////////////////////
DeferredRequestCreator deferredRequestCreator =
targetToDeferredRequestCreator.remove(targetImageView);
if (deferredRequestCreator != null) {
deferredRequestCreator.cancel();
}
//////////////////////////// defer cancel ///////////////////////////
}
}
我们着重看一下:targetToAction
final Map<Object, Action> targetToAction;
他是一个map,so有了map这个容器,我们来管理 target 这个key 对应的所有 action 岂不是非常容易!
当然相关的 任务 的执行是由 final Dispatcher dispatcher; 来管理的。这个下面细说。
再次判断,该target若是 imgeview,取消相关的其延迟请求,这个延迟(defer),后面说。
c. defer
defer: 延迟,延期。
为什么会有这个概念?
看下 RequestCreator中的这个方法
/**
Attempt to resize the image to fit exactly into the target ImageView's bounds.
This will result in delayed execution of the request until the ImageView has been laid out.
Note: This method works only when your target is an ImageView.
*/
public RequestCreator fit() {
deferred = true;
////////////////////////////// return this ///////////////////////////
return this;
}
你下载下来的 bitmap的大小和 imageview的大小不匹配。为了让bitmap适应 imageview的大小。
等你imageview被布局完毕后(imagview的大小固定了),这个延迟的请求才会被执行。这样他们就fit了。
Picasso 这个大管家。接手了 imagview 这个target的 延迟请求:
picasso.defer(target, new DeferredRequestCreator(this, target, callback));
---------------
void defer(ImageView view, DeferredRequestCreator request) {
// If there is already a deferred request, cancel it.
if (targetToDeferredRequestCreator.containsKey(view)) {
cancelExistingRequest(view);
}
targetToDeferredRequestCreator.put(view, request);
}
---------------------------------
final Map<ImageView, DeferredRequestCreator> targetToDeferredRequestCreator;// 是个map哦。
d. 创建 Request 和 request key
- 创建request
/**
* Create the request optionally passing it through the request transformer.
*/
private Request createRequest(long started) {
int id = nextId.getAndIncrement();
Request request = data.build();
request.id = id;// 一个request,一个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;
}
builder 创建了request(request需要好多信息啊!)
/** Create the immutable {@link Request} object. */
public Request build() {
if (centerInside && centerCrop) {
throw new IllegalStateException("Center crop and center inside can not be used together.");
}
if (centerCrop && (targetWidth == 0 && targetHeight == 0)) {
throw new IllegalStateException(
"Center crop requires calling resize with positive width and height.");
}
if (centerInside && (targetWidth == 0 && targetHeight == 0)) {
throw new IllegalStateException(
"Center inside requires calling resize with positive width and height.");
}
if (priority == null) {
priority = Priority.NORMAL;
}
// 需要好多信息啊? 什么时候要使用这些信息呢???
return new Request(uri, resourceId, stableKey, transformations, targetWidth, targetHeight,
centerCrop, centerInside, onlyScaleDown, rotationDegrees, rotationPivotX, rotationPivotY,
hasRotationPivot, purgeable, config, priority);
}
}
Request 有着唯一的 key(String类型),key的生成规则:
static String createKey(Request data) {
String result = createKey(data, MAIN_THREAD_KEY_BUILDER);
MAIN_THREAD_KEY_BUILDER.setLength(0);
return result;
}
-------------
static String createKey(Request data, StringBuilder builder) {
//-------------------------------------> 1
if (data.stableKey != null) {// 1. stable key
builder.ensureCapacity(data.stableKey.length() + KEY_PADDING);
builder.append(data.stableKey);
} else if (data.uri != null) {// 2. uri
String path = data.uri.toString();
builder.ensureCapacity(path.length() + KEY_PADDING);
builder.append(path);
} else {// 3. resource id
builder.ensureCapacity(KEY_PADDING);
builder.append(data.resourceId);
}
builder.append(KEY_SEPARATOR);
//---------------------------------------> 2
if (data.rotationDegrees != 0) {
builder.append("rotation:").append(data.rotationDegrees);
if (data.hasRotationPivot) {
builder.append('@').append(data.rotationPivotX).append('x').append(data.rotationPivotY);
}
builder.append(KEY_SEPARATOR);
}
//---------------------------------------> 3
if (data.hasSize()) {
builder.append("resize:").append(data.targetWidth).append('x').append(data.targetHeight);
builder.append(KEY_SEPARATOR);
}
//---------------------------------------> 4
if (data.centerCrop) {
builder.append("centerCrop").append(KEY_SEPARATOR);
} else if (data.centerInside) {
builder.append("centerInside").append(KEY_SEPARATOR);
}
//---------------------------------------> 5
if (data.transformations != null) {
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = data.transformations.size(); i < count; i++) {
builder.append(data.transformations.get(i).key());
builder.append(KEY_SEPARATOR);
}
}
return builder.toString();
}
e. Action
一个request被包装成了 action 被执行。
Action action =
new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
errorDrawable, requestKey, tag, callback, noFade);
picasso.enqueueAndSubmit(action);
现在问题来了:
- action 是什么?
- action 是什么?
- action 是什么?
看一下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;
包裹里这么多信息,他到底想干嘛?
再看下他定义的方法:
///////////////////////////////////////////////////////////////
abstract void complete(Bitmap result, Picasso.LoadedFrom from);
abstract void error();
void cancel() {
cancelled = true;
}
///////////////////////////////////////////////////////////////
Request getRequest() {
return request;
}
T getTarget() {
return target == null ? null : target.get();
}
String getKey() {
return key;
}
boolean isCancelled() {
return cancelled;
}
boolean willReplay() {
return willReplay;
}
int getMemoryPolicy() {
return memoryPolicy;
}
int getNetworkPolicy() {
return networkPolicy;
}
Picasso getPicasso() {
return picasso;
}
Priority getPriority() {
return request.priority;
}
从以上方法猜测,action关注:
他包裹的请求被 处理完成后(complete),或是失败后的动作处理(error),或是 cancel的处理
再看action的子类:
我们关注 ImageViewAction,看下他怎么处理 complete,error,cancel:
@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;
}
Drawable placeholder = target.getDrawable();
if (placeholder instanceof AnimationDrawable) {
((AnimationDrawable) placeholder).stop();
}
if (errorResId != 0) {
target.setImageResource(errorResId);
} else if (errorDrawable != null) {
target.setImageDrawable(errorDrawable);
}
if (callback != null) {
callback.onError();
}
}
---------------------
@Override void cancel() {
super.cancel();
if (callback != null) {
callback = null;
}
}
接下来我们来关注,这个action由谁来操作的。
f. 管理action
你猜是谁?
Picasso 这个大管家
picasso.enqueueAndSubmit(action);
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); // action与 target相关联
}
submit(action);
}
void submit(Action action) { dispatcher.dispatchSubmit(action);}
dispatcher 来处理这个action。
dispatcher 由Picasso持有,它的构建在Picasso 的builer下构建(在build下我也提到过~)
那么问题来了:
Dispatcher是什么?
留待下文分解。