Picasso(四into设置图片方法解析)
这就是具体的设置图片的操作
public void into (ImageView target, Callback callback){
long started = System.nanoTime();
// 通过当前的Looper来判断是否在主线程
checkMain();
// 当前的ImageView不能为空
if (target == null) {
throw new IllegalArgumentException("Target must not be null.");
}
// 通过uri或者资源id判断,如果没有图片信息就取消当前的请求,并设置占位图片
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);
}
// 这里主要是如果之前自定义了transform,会发生在这个方法(之前说过我们为您一般也不需要定义这个东西)
Request request = createRequest(started);
// 这里其实主要是将request和key关联,和我们之前说key可以理解为标识就有了联系
String requestKey = createKey(request);
// 是否在内存缓存中读取数据
if (shouldReadFromMemoryCache(memoryPolicy)) {
// 从缓存中获取bitmap
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
if (bitmap != null) {
// 取到bitmap,就把网络请求撤销
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);
}
其中的cancelRequest方法回调用cancelExistingRequest(Action就在这里简单说明一下,其实它就是request的包装类,还包括Picasso对象,缓存策略等)
void cancelExistingRequest(Object target) {
// 判断是否在主线程
checkMain();
// 这个map是将请求对象和请求关联起来(object 比如ImageView),移除请求对象
Action action = targetToAction.remove(target);
if (action != null) {
// 同时移除请求
action.cancel();
// 通过dispatcher分发器,用子线程分发器的handler来取消请求(关于这个,在get方法中描述过)
dispatcher.dispatchCancel(action);
}
if (target instanceof ImageView) {
// 转换成需要的ImageView
ImageView targetImageView = (ImageView) target;
// DeferredRequestCreator其实就是RequestCreator的包装类,是为了对ImageView的
//监听,跟进去,会发现getViewTreeObserver来监听,获取如具体的宽高等,因此也需要移除。
DeferredRequestCreator deferredRequestCreator =
targetToDeferredRequestCreator.remove(targetImageView);
if (deferredRequestCreator != null) {
deferredRequestCreator.cancel();
}
}
}
以上用到了两个比较重要的类,BitmapHunter和Action,这里我们简单解释以下
BitmapHunter实现了Runnable接口,我们要关注的是他的run方法
@Override public void run() {
try {
updateThreadName(data);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
}
// 获取图片(首先通过内存,不行在通过网络,用okhttp3)
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);
} 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);
}
}
可以看到,在run方法中主要就是获取我们需要的Bitmap,通过dispatcher调用方法,在子线程中通过Handler来进行操作。这里我们需要理解的是,获取图片,以及获取成功后是否缓存,失败后怎么处理的一系列操作,这些都是通过Dispatcher中的handler来处理了的。
Action
这里就不再赘述了,就是request的包装类,其中包含了很多的其他东西,比如缓存策略,Picasso对象,等。
最后我们跟进去最后的提交任务
void enqueueAndSubmit(Action action) {
// 获取请求操作的对象
Object target = action.getTarget();
// 判断object和action是否匹配(这个object可以是ImageView,而action又是
//request的包装类,这一下,两者的结合就比较明白了(请求操作对象和具体的请求)
if (target != null && targetToAction.get(target) != action) {
// This will also check we are on the main thread.
// 发现不匹配,取消请求
cancelExistingRequest(target);
// 将此时两者关联(也就是放在一个map集合当中)
targetToAction.put(target, action);
}
// 继续提交
submit(action);
}
这个方法就是object和action的校验检查。最后我们继续追踪,会发现又是通过dispatcher所在子线程的handler来进行处理,最后一直调用performSubmit方法
void performSubmit(Action action, boolean dismissFailed) {
// 判断是否延迟暂停加载(就是之前是否设置过暂停延迟的标记)
if (pausedTags.contains(action.getTag())) {
// 同样将target和action关联(此时存放发延迟加载),存放以便于后来的唤醒
pausedActions.put(action.getTarget(), action);
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),
"because tag '" + action.getTag() + "' is paused");
}
return;
}
// 获取请求的图片捕获器(就是一个runnable,说过,key可以当成bitmap和请求的标记)
BitmapHunter hunter = hunterMap.get(action.getKey());
if (hunter != null) {
// 这里是对hunter的action初始化并进行健壮性判断
hunter.attach(action);
return;
}
// 判断当前线程池是否关闭
if (service.isShutdown()) {
// 如果关闭,打印日志,直接结束
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
}
return;
}
// 这个方法主要是找到能够处理相应请求request的requestHandler,并封装成BitmapHuntere返回
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
// 交给线程池处理(获取图片),关注BitmapHunter的run方法(之前讲过了)
hunter.future = service.submit(hunter);
// 关联,将key和hunter关联
hunterMap.put(action.getKey(), hunter);
if (dismissFailed) {
failedActions.remove(action.getTarget());
}
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
}
}
当这一切都完成之后就可以设置图片,加载成功了。
总结
在这个方法中,我们需要理解和知道的是,我们首先进行一些判断,是否在主线程,object的空指针和匹配为题等,在一些健壮性判断完成之后,开始建立请求提交,submit,以及我们的一些标记和建立联系也都是贯穿建立的,也是通过分发handler来完成请求,从内存到网络,最后提交成功,完成显示。核心,就是任务的提交。
此次Picasso的感受:
这个设计非常nice的地方就是
1.通过构建者模式收集我们设置的属性,同时完成一些初始化,我们最后就可以通过Builder获取我们想要的属性。
2.同时也是其他框架的一些共同点,就是封装。确实这里面很多的封装类设计的非常好,比如Dispatcher,BitmapHunter,框架中非常突出的核心类。在传递的过程中,很精简,类中包括了我们要的信息,不需要过多的拆分,调用麻烦,或者不拆分,耦合严重。这是值得我们学习的。
2.同时也是最终要的,就是dispatcher和main的两个handler,整个框架都是通过handler来完成事件的分发和传递。也就说明,以后在我们的项目中Handler确实要善用,合理的分配。