Picasso源码分析(一):单例模式、建造者模式、面向接口编程
Picasso源码分析(二):默认的下载器、缓存、线程池和转换器
Picasso源码分析(三):快照功能实现和HandlerThread的使用
Picasso源码分析(四):不变模式、建造者模式和Request的预处理
Picasso源码分析(五):into方法追本溯源和责任链模式创建BitmapHunter
Picasso源码分析(六):BitmapHunter与请求结果的处理
into方法首先检查了是否在主线程,控件是否为null,用户有没有设置uri或者资源id,是否调用了fit方法自适应压缩。接着根据into方法调用时间创建Request请求,并为该请求设置缓存用的key。接着判断是否要读内存缓存,可以读内存缓存并且命中的话就将缓存中的图片显示在控件然后返回,否则开启网络请求。网络请求的过程中暂时显示占位图。
网络请求的过程就是针对此次请求封装成一个ImageViewAction的请求动作,通过Picasso将此动作递交给调度器dispatcher,在dispatcher中进行线程切换,在工作线程中处理该动作。处理的方法就是为这个动作创建一个BitmapHunter(实现了Runnable接口),然后把这个BitmapHunter交给线程池去处理。
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 (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);
}
}
run方法在设置了线程名后,直接调用hunt方法获取请求结果。如果hunt方法返回null,说明请求失败,调用调度器dispatchFailed方法,否则说明请求成功,调用调度器的dispatchComplete方法。当然在网络请求的过程中会出现各种异常,run方法均一一捕获并处理。
在finally语句块中会重置线程名,表示该线程任务已经完成。
具体的网络请求过程是交给了hunt方法处理,该方法返回请求到的Bitmap,网络请求中出现的异常直接抛出,交给调用者也就是run方法处理。
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;
}
}
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 = 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;
}
hunt方法首先根据内存策略判断是不是可以读取缓存,可以的话就直接读缓存,缓存命中直接返回命中的bitmap,否则需要进行网络请求。
请求前先设置缓存策略,retryCount为0的话表示只能读取缓存不能尽情网络请求,否则的话缓存策略就用Request的缓存策略。
data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
接着就会调用RequestHandler实现类的load方法进行具体的请求过程。RequestHandler有多个实现类。
BitmapHunter使用哪一个RequestHandler具体在创建BitmapHunter的时候已经通过责任链模式指定
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);
}
}
如果是网络请求的话,实际上是交给了NetworkRequestHandler进行网络请求。NetworkRequestHandler的load方法是直接交给了downloader的load方法,downloader的类型是Downloader类型的接口,Downloader接口有两个实现类,基于okhttp的OkHttpDownloader和基于HttpURLConnection的UrlConnectionDownloader。
@Override
public Result load(Request request, int networkPolicy) throws IOException {
Response response = downloader.load(request.uri, request.networkPolicy);
...
请求成功的话会对请求到的结果进行处理,如果请求的结果中没有Bitmap对象,那么就从流中把Bitmap解析出来。
解析到Bitmap后,还要对Bitmap进行各种变换处理,最终将变换处理后的Bitmap返回。
hunt方法上的请求结果会反馈给调度器
result = hunt();
if (result == null) {
dispatcher.dispatchFailed(this);
} else {
dispatcher.dispatchComplete(this);
}
调度器通过handler切换线程在工作线程对请求结果进行处理
void dispatchComplete(BitmapHunter hunter) {
handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
}
@Override public void handleMessage(final Message msg) {
switch (msg.what) {
...
case HUNTER_COMPLETE: {
BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performComplete(hunter);
break;
}
...
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");
}
}
performComplete方法先根据缓存策略进行缓存的更新或保存,接着从map中删除此请求。接下来有个问题,此时是在工作线程进行的处理,如果要将请求结果显示在控件上,需要切换到UI主线程。如果通过handler进行线程切换,那么handler应该和被切换到的线程的looper绑定在一起,因此需要一个和主线程的looper绑定在一起的handler进行线程切换,将结果切换到主线程。
接下来看batch方法的处理
private void batch(BitmapHunter hunter) {
if (hunter.isCancelled()) {
return;
}
batch.add(hunter);
if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
}
}
batch方法给工作线程的handler发送了HUNTER_DELAY_NEXT_BATCH消息,所以在工作线程的handleMessage方法中对此消息进行处理
case HUNTER_DELAY_NEXT_BATCH: {
dispatcher.performBatchComplete();
break;
}
那么应该是在performBatchComplete()方法中完成了线程的切换
void performBatchComplete() {
List copy = new ArrayList(batch);
batch.clear();
mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
logBatch(copy);
}
果不其然,这里通过mainThreadHandler给主线程发送了HUNTER_BATCH_COMPLETE消息,那么在主线程的handleMessage方法中会处理此消息
case HUNTER_BATCH_COMPLETE: {
@SuppressWarnings("unchecked") List batch = (List) msg.obj;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, n = batch.size(); i < n; i++) {
BitmapHunter hunter = batch.get(i);
hunter.picasso.complete(hunter);
}
break;
}
而在主线程中调用的complete方法中,会调用deliverAction(result, from, join)方法,deliverAction方法调用了action.complete(result, from),将请求到的图片显示到了控件上。