BitmapHunter实现了Runnable接口。正如其名字,BitmapHunter负责获取Bitmap。此外,BitmapHunter还提供了几个静态工具方法。
创建BitmapHunter实例
RequestHandler是BitmapHunter的核心成员变量,它负责如何从磁盘或者网络获取Bitmap。BitmapHunter的静态方法forRequest选取合适的RequestHandler来创建BitmapHunter实例。
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
Action action) {
Request request = action.getRequest();
List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
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);
}
复制代码
hunt方法
BitmapHunter获取Bitmap和转换Bitmap的逻辑都集中在该方法中。
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
// 根据内存换成策略判断是否读取内存缓存
if (shouldReadFromMemoryCache(memoryPolicy)) {
bitmap = cache.get(key);
// 如果读取内存且能读取到Bitmap则返回Bitmap
if (bitmap != null) {
stats.dispatchCacheHit();
loadedFrom = MEMORY;
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
}
return bitmap;
}
}
// 根据RequestHandler的retryCount重置Request的网络策略。
// 如果RequestHandler的retryCount为0,则Request的网络策略为OFFLINE,即只读取磁盘缓存,不发送网络请求。
data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
// 如果不读取内存缓存或内存缓存读取不到,则交给RequestHandler处理。这里data的networkPolicy可能和networkPolicy不一致。
RequestHandler.Result result = requestHandler.load(data, networkPolicy);
// 如果RequestHandler成功处理了Request并且返回的Result不为空。
if (result != null) {
loadedFrom = result.getLoadedFrom();
exifRotation = result.getExifOrientation();
bitmap = result.getBitmap();
// RequestHandler的结果Result要么能直接得到Bitmap要么是得到InputStream
if (bitmap == null) {
InputStream is = result.getStream();
try {
// 如果Result得到的是InputStream,那么就把InputStream转化为Bitmap。
bitmap = decodeStream(is, data);
} finally {
Utils.closeQuietly(is);
}
}
}
// 如果能从RequestHandler的Result中得到Bitmap,接下来就是根据Request对Bitmap做变换。
if (bitmap != null) {
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId());
}
stats.dispatchBitmapDecoded(bitmap);
if (data.needsTransformation() || exifRotation != 0) {
// DECODE_LOCK是一个全局的锁,只是一个简单的静态Object对象。注释里说这是借鉴了Volley。
// 因为对Bitmap做变换需要占用较多内存,所以这里加了锁,保证每次只有一条线程在做Bitmap变换,避免发生OOM。
synchronized (DECODE_LOCK) {
if (data.needsMatrixTransform() || exifRotation != 0) {
// 根据Request对Bitmap做缩放,旋转等变换。
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);
}
}
}
// 最后,返回从RquestHandler获取且做了相应转换的Bitmap。
return bitmap;
}
复制代码
同步与异步
如果想同步获取Bitmap则调用BitmapHunter的hunt方法。 如果想异步获取Bitmap则把BitmapHunter提交到线程池中执行。BitmapHunter实现了Runnable接口,异步获取Bitmap的逻辑在方法run中。run方法的逻辑比较简单,在调用hunt方法的前后加了一些操作。
@Override public void run() {
try {
// 把当前线程名更改为Request的name。
updateThreadName(data);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
}
// 用hunt方法获取Bitmap
result = hunt();
// 把获取结果交给Dispatcher处理
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_IDLE_NAME
Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);
}
}
//把当前线程名改为Request的name.
static void updateThreadName(Request data) {
String name = data.getName();
// NAME_BUILDER是ThreadLocal对象。
StringBuilder builder = NAME_BUILDER.get();
builder.ensureCapacity(Utils.THREAD_PREFIX.length() + name.length());
builder.replace(Utils.THREAD_PREFIX.length(), builder.length(), name);
Thread.currentThread().setName(builder.toString());
}
复制代码
decodeStream
decodeStream方法是BitmapHunter的一个公共静态方法,用来把字节流InputStream解析成Bitmap。该方法会考虑Request的需求(比如inSampleSize)来更有效地解析InputStream。该方法还可以解析WEBP格式的图片。
static Bitmap decodeStream(InputStream stream, Request request) throws IOException {
// 这里把InputStream封装成MarkableInputStream对象。
// MarkableInputStream是Picasso提供的可标记的InputStream,可以多次从某个位置开始读取。类似有Java提供的PushbackInputStream。
MarkableInputStream markStream = new MarkableInputStream(stream);
stream = markStream;
// 在第65536个字节出做标记,后面读取InputStream不能超过这个范围。
long mark = markStream.savePosition(65536);
// 根据Request获取BitmapFactory.Options和判断是否要压缩图片
final BitmapFactory.Options options = RequestHandler.createBitmapOptions(request);
final boolean calculateSize = RequestHandler.requiresInSampleSize(options);
// 判断是否是WEBP图片
boolean isWebPFile = Utils.isWebPFile(stream);
// 读取头部的12个字节后,把游标恢复到InputStream开始处
markStream.reset(mark);
// 因为BitmapFactory的decodeStream方法在解析WEBP格式图片时,会抛出JNI异常,所以这里使用decodeByteArray方法。
if (isWebPFile) {
// 先把InputStream转化为byte[]
byte[] bytes = Utils.toByteArray(stream);
if (calculateSize) {
// 根据Request的目标宽高计算压缩比例
BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
request);
}
// 调用BitmapFactory.decodeByteArray的方法解析出Bitmap
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
} else {
if (calculateSize) {
// 根据Request的目标宽高计算压缩比例
BitmapFactory.decodeStream(stream, null, options);
RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
request);
// 把游标恢复到InputStream开始处,以便后面的decodeStream,从头读取。
markStream.reset(mark);
}
// 调用BitmapFactory.decodeStream的方法解析出Bitmap
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
if (bitmap == null) {
// 如果Bitmap为空,则抛出异常,后续操作会重试
throw new IOException("Failed to decode stream.");
}
return bitmap;
}
}
static boolean isWebPFile(InputStream stream) throws IOException {
byte[] fileHeaderBytes = new byte[WEBP_FILE_HEADER_SIZE];
boolean isWebPFile = false;
if (stream.read(fileHeaderBytes, 0, WEBP_FILE_HEADER_SIZE) == WEBP_FILE_HEADER_SIZE) {
// WEBP文件的头部是12个字节,前4个字节是“RIFF”,中间4个字节是文件大小,后4个字节是“WEBP”。
isWebPFile = WEBP_FILE_HEADER_RIFF.equals(new String(fileHeaderBytes, 0, 4, "US-ASCII"))
&& WEBP_FILE_HEADER_WEBP.equals(new String(fileHeaderBytes, 8, 4, "US-ASCII"));
}
return isWebPFile;
}
复制代码
transformResult
根据Request的目标宽高、scaleType、 旋转角度和旋转点、exifRotation,用矩阵Matrix进行变换。
static Bitmap transformResult(Request data, Bitmap result, int exifRotation) {
int inWidth = result.getWidth();
int inHeight = result.getHeight();
boolean onlyScaleDown = data.onlyScaleDown;
int drawX = 0;
int drawY = 0;
int drawWidth = inWidth;
int drawHeight = inHeight;
Matrix matrix = new Matrix();
if (data.needsMatrixTransform()) {
int targetWidth = data.targetWidth;
int targetHeight = data.targetHeight;
float targetRotation = data.rotationDegrees;
if (targetRotation != 0) {
if (data.hasRotationPivot) {
matrix.setRotate(targetRotation, data.rotationPivotX, data.rotationPivotY);
} else {
matrix.setRotate(targetRotation);
}
}
if (data.centerCrop) {
float widthRatio = targetWidth / (float) inWidth;
float heightRatio = targetHeight / (float) inHeight;
float scaleX, scaleY;
if (widthRatio > heightRatio) {
int newSize = (int) Math.ceil(inHeight * (heightRatio / widthRatio));
drawY = (inHeight - newSize) / 2;
drawHeight = newSize;
scaleX = widthRatio;
scaleY = targetHeight / (float) drawHeight;
} else {
int newSize = (int) Math.ceil(inWidth * (widthRatio / heightRatio));
drawX = (inWidth - newSize) / 2;
drawWidth = newSize;
scaleX = targetWidth / (float) drawWidth;
scaleY = heightRatio;
}
if (shouldResize(onlyScaleDown, inWidth, inHeight, targetWidth, targetHeight)) {
matrix.preScale(scaleX, scaleY);
}
} else if (data.centerInside) {
float widthRatio = targetWidth / (float) inWidth;
float heightRatio = targetHeight / (float) inHeight;
float scale = widthRatio < heightRatio ? widthRatio : heightRatio;
if (shouldResize(onlyScaleDown, inWidth, inHeight, targetWidth, targetHeight)) {
matrix.preScale(scale, scale);
}
} else if ((targetWidth != 0 || targetHeight != 0) //
&& (targetWidth != inWidth || targetHeight != inHeight)) {
float sx =
targetWidth != 0 ? targetWidth / (float) inWidth : targetHeight / (float) inHeight;
float sy =
targetHeight != 0 ? targetHeight / (float) inHeight : targetWidth / (float) inWidth;
if (shouldResize(onlyScaleDown, inWidth, inHeight, targetWidth, targetHeight)) {
matrix.preScale(sx, sy);
}
}
}
if (exifRotation != 0) {
matrix.preRotate(exifRotation);
}
Bitmap newResult =
Bitmap.createBitmap(result, drawX, drawY, drawWidth, drawHeight, matrix, true);
if (newResult != result) {
// 如果进行了变换,则对原Bitmap进行回收,避免OOM。
result.recycle();
result = newResult;
}
return result;
}
复制代码
applyCustomTransformations
按照添加顺序依次使用自定义的Transformation,对Bitmap进行变换。如果中间某个环节出错,则返回null,停止继续变换。
static Bitmap applyCustomTransformations(List<Transformation> transformations, Bitmap result) {
for (int i = 0, count = transformations.size(); i < count; i++) {
final Transformation transformation = transformations.get(i);
Bitmap newResult;
try {
newResult = transformation.transform(result);
} catch (final RuntimeException e) {
// 如果某个Transformation转换过程抛出异常,则返回null,停止继续变换。
Picasso.HANDLER.post(new Runnable() {
@Override public void run() {
throw new RuntimeException(
"Transformation " + transformation.key() + " crashed with exception.", e);
}
});
return null;
}
// 如果变换结果为null,则返回null,停止继续变换。
if (newResult == null) {
final StringBuilder builder = new StringBuilder() //
.append("Transformation ")
.append(transformation.key())
.append(" returned null after ")
.append(i)
.append(" previous transformation(s).\n\nTransformation list:\n");
for (Transformation t : transformations) {
builder.append(t.key()).append('\n');
}
Picasso.HANDLER.post(new Runnable() {
@Override public void run() {
throw new NullPointerException(builder.toString());
}
});
return null;
}
// 如果变换后还是原Bitmap对象,而且原Bitmap对象已回收,则返回null,停止继续变换。
if (newResult == result && result.isRecycled()) {
Picasso.HANDLER.post(new Runnable() {
@Override public void run() {
throw new IllegalStateException("Transformation "
+ transformation.key()
+ " returned input Bitmap but recycled it.");
}
});
return null;
}
// 如果变换得到新的Bitmap对象,且旧Bitmap对象没有回收,则返回null,停止继续变换。
// 所以,如果Transformation的变换结果是新的Bitmap对象,要记得回收旧Bitmap对象。
if (newResult != result && !result.isRecycled()) {
Picasso.HANDLER.post(new Runnable() {
@Override public void run() {
throw new IllegalStateException("Transformation "
+ transformation.key()
+ " mutated input Bitmap but failed to recycle the original.");
}
});
return null;
}
result = newResult;
}
return result;
}
复制代码
优先级
BitmapHunter的成员变量priority和sequence共同决定了其在线程池任务队列中的优先级。优先比较priority,如果两个BitmapHunter的priority相同则考虑sequence。priority是取BitmapHunter的主Action和3个次Action中priority值最高的那个。sequence则是BitmapHunter的创建序号,由AtomicInteger产生。
Action
一个BitmapHunter对象可以对应多个相同的请求。BitmapHunter用成员变量action来保存主Action和一个容量为3的List来保存此Action。
当重复提交一个Action时,BitmapHunter的attach方法会被调用。该方法会保持提交的Action,并重置成员变量priority。
void attach(Action action) {
boolean loggingEnabled = picasso.loggingEnabled;
Request request = action.request;
// 如果主action为空,则把新action是主action。
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;
}
// 如果已有主action,且次action列表为空,则新建一个容量为3的List。
if (actions == null) {
actions = new ArrayList<Action>(3);
}
// 添加新action。
actions.add(action);
if (loggingEnabled) {
log(OWNER_HUNTER, VERB_JOINED, request.logId(), getLogIdsForHunter(this, "to "));
}
Priority actionPriority = action.getPriority();
// 如果新action的priority大于当前的priority,则把priority设为新action的priority。
if (actionPriority.ordinal() > priority.ordinal()) {
priority = actionPriority;
}
}
复制代码
当取消一个Action时,BitmapHunter的detach方法会被调用,删除取消的Action,并根据剩下的Action计算成员变量priority。
void detach(Action action) {
//判断是否要删除action
boolean detached = false;
if (this.action == action) {
this.action = null;
detached = true;
} else if (actions != null) {
detached = actions.remove(action);
}
// 如果删除了action,则重新计算priority。
if (detached && action.getPriority() == priority) {
priority = computeNewPriority();
}
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_REMOVED, action.request.logId(), getLogIdsForHunter(this, "from "));
}
}
private Priority computeNewPriority() {
Priority newPriority = LOW;
boolean hasMultiple = actions != null && !actions.isEmpty();
boolean hasAny = action != null || hasMultiple;
// 如果没有action,则返回最低优先级。
if (!hasAny) {
return newPriority;
}
// 优先级是取四个action中最大的那个
if (action != null) {
newPriority = action.getPriority();
}
if (hasMultiple) {
for (int i = 0, n = actions.size(); i < n; i++) {
Priority actionPriority = actions.get(i).getPriority();
if (actionPriority.ordinal() > newPriority.ordinal()) {
newPriority = actionPriority;
}
}
}
return newPriority;
}
复制代码
取消任务
当想取消任务时,BitmapHunter的cancel方法会被调用。只有当主action为空,次action列表为空,且BitmapHunter提交到线程池后返回的Future对象的cancel方法返回true时,任务才能被取消。
boolean cancel() {
return action == null
&& (actions == null || actions.isEmpty())
&& future != null
&& future.cancel(false);
}
复制代码