Picasso源代码分析:3、BitmapHunter

前两篇文章当中,我们依次分析了根据数据的流程,即我们调用的代码

Picasso.with(Context).load(imageUrl).into(ImageView)

依次分析了Picasso.java和ReqeustCreator.java的源代码,其中对Reqeust,Action,Target等等也有了一定的了解,接下来干嘛呢? 其实到现在为止,我们分析的,都是任务的创建和提交,提交到队列之后干嘛呢?在ReqeustCreator当中有一个同步的请求方法Bitmap get()方法,在该方法中,最后的代码依次是创建了一个BitmapHunter,然后调用了其中的hunt()方法,因此我们可以大体猜出,该类应该是负责执行具体的请求的类。我们先不去管具体的任务是怎么样调度的,先来分析一下这个类。BitmapHunter.java

BitmapHunter

首先看到,该类实现了Runnable接口,也就是说,在正常的情况下,其中的run()方法应该是程序的主要入口,Executor或者Thread调用该线程,并执行其中的run方法。我们依次来分析。 静态变量:

//一个全局锁,用来保证在同一个时刻只有一张图片在解码。为了减少内存消耗,哈哈,原来这个地方是直接从Volley那里学来的,我说怎么一个样子。
private static final Object DECODE_LOCK = new Object();

private static final ThreadLocal NAME_BUILDER = new ThreadLocal() {
@Override protected StringBuilder initialValue() {
return new StringBuilder(Utils.THREAD_PREFIX);
}
};

private static final AtomicInteger SEQUENCE_GENERATOR = new AtomicInteger();

private static final RequestHandler ERRORING_HANDLER = new RequestHandler() {
@Override public boolean canHandleRequest(Request data) {
return true;
}

@Override public Result load(Request data) throws IOException {
  throw new IllegalStateException("Unrecognized type of request: " + data);
}

};

  • 在BitmapHunter当中也有一个全局锁,DECODE_LOCK,用来保证任何时刻最多只有一个图片正在解码,作者说这是从Volley中学来的。
  • NAME_BUILDER,是一个ThreadLocal,ThreadLocal大约是Thread Local Variable的意思,也就是说,在每个线程中,都有唯一的一个NAME_BUILDER的副本,等以后用的时候再去分析。
  • SEQUENCE_GENERATOR:序列号生成器,原子变量,确保多线程中能够正确的生成需要的序列号。
  • ERRORING_HANDLER:一个默认的用来处理错误的HANDLER,默认的实现办法是抛出异常。

成员变量: 类中的成员变量我们先不看,等用的时候再哎看看是怎么定以的就可以。 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) {
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()方法中都做了什么事情?

  1. 更新线程名称
  2. 调用hunt()方法获取请求结果。
  3. 根据结果result派发dispatcher.dispatchFailed(this);或者dispatcher.dispatchComplete(this);

所以重点还是在Bitmap hunt()方法当中

Bitmap hunt() throws IOException {
Bitmap bitmap = null;

//如果不跳过MemoryCache,那么则尝试从MemoryCache获取
if (!skipMemoryCache) {
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.loadFromLocalCacheOnly = (retryCount == 0);
RequestHandler.Result result = requestHandler.load(data);
if (result != null) {
bitmap = result.getBitmap();
//从枚举类获取信息,是从Network,Disk,还是Memory中获取到的数据
loadedFrom = result.getLoadedFrom();
//是否需要旋转
exifRotation = result.getExifOrientation();
}

if (bitmap != null) {
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId());
}
//(错误理解!)派发数据—->奇怪,是什么先派发Decoded完成,再派发Transformed,如果需要Transformed,那么Decoded是否是重复工作了呢?
//实际上,并不是派发数据,而是通知统计线程,完成该图片的下载和解码。
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()方法主要进行了以下的工作:

  1. 判断是否跳过MemoryCache,如果不跳过,那么就尝试从MemoryCache中获取数据。
  2. 然后调用Reqeust对应的ReqeustHandler去下载数据并解码为Bitmap
  3. 通知统计线程更新统计信息
  4. 如果需要Transformation或者旋转,那么则依次调用Transformation还有旋转
    1. 需要注意的是,此处同样是对Bitmap进行操作,因此也会消耗大量的内存空间,所以Picasso也是使用了同样一个锁DECODE_LOCK来保证同一个时刻仅仅有一个图片正在处理。
    2. 之前文章《Picasso的使用:使用Transformation,下载后预处理图片并显示》中,我们在ReqeustCreator中添加了一个自定义的Transformation,就在这个阶段会被调用。

void attach(Action action) 将action合并到当前实例当中,实际上我理解的就是Volley的ImageLoader里面的BatchedImageReqeust一样,将多个同样地址的请求打包在一起进行处理。其代码如下:

void attach(Action action) {
boolean loggingEnabled = picasso.loggingEnabled;
Request request = action.request;

if (this.action == null) {
this.action = action;
if (loggingEnabled) {
if (actions == null || actions.isEmpty()) {
log(OWNER_HUNTER, VERB_JOINED, request.logId(), “to empty hunter”);
} else {
log(OWNER_HUNTER, VERB_JOINED, request.logId(), getLogIdsForHunter(this, “to “));
}
}
return;
}

if (actions == null) {
actions = new ArrayList(3);
}

actions.add(action);

if (loggingEnabled) {
log(OWNER_HUNTER, VERB_JOINED, request.logId(), getLogIdsForHunter(this, “to “));
}

//更新优先级
Priority actionPriority = action.getPriority();
if (actionPriority.ordinal() > priority.ordinal()) {
priority = actionPriority;
}
}

其代码比较简单,主要包括两部分的功能:

  • 将action添加到成员变量actions当中
  • 修正优先级。以actions当中优先级最高的作为整个BitmapHunter的优先级。

BitmapHunter大体的思路就是这样,下面会继续分析ReqeustHandler以及Delivery的相关代码。