Picasso源码阅读笔记五

BitmapHunter是Picasso中的核心组件之一,负责加载和处理Bitmap资源。它实现了Runnable接口,可以通过不同的RequestHandler来获取Bitmap,并提供了一系列静态工具方法辅助Bitmap的加载与转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
}
复制代码

转载于:https://juejin.im/post/5a7ef8956fb9a063606ed28c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值