/** Performs the decode synchronously. */
    private void doDecode(EncodedImage encodedImage, boolean isLast) {
      if (isFinished() || !EncodedImage.isValid(encodedImage)) {
        return;
      }

      try {
        long queueTime = mJobScheduler.getQueuedTime();
        int length = isLast ? encodedImage.getSize() : getIntermediateImageEndOffset(encodedImage);
        QualityInfo quality = isLast ? ImmutableQualityInfo.FULL_QUALITY : getQualityInfo();

        mProducerListener.onProducerStart(mProducerContext.getId(), PRODUCER_NAME);
        CloseableImage image = null;
        try {
          image = mImageDecoder.decodeImage(encodedImage, length, quality, mImageDecodeOptions);
        } catch (Exception e) {
          Map<String, String> extraMap = getExtraMap(image, queueTime, quality, isLast);
          mProducerListener.onProducerFinishWithFailure(
              mProducerContext.getId(), PRODUCER_NAME, e, extraMap);
          handleError(e);
          return;
        }
        Map<String, String> extraMap = getExtraMap(image, queueTime, quality, isLast);
        mProducerListener.onProducerFinishWithSuccess(
            mProducerContext.getId(), PRODUCER_NAME, extraMap);
        handleResult(image, isLast);
      } finally {
        EncodedImage.closeSafely(encodedImage);
      }
    }
 /**
  * Finishes if not already finished and <code>shouldFinish</code> is specified.
  *
  * <p>If just finished, the intermediate image gets released.
  */
 private void maybeFinish(boolean shouldFinish) {
   synchronized (ProgressiveDecoder.this) {
     if (!shouldFinish || mIsFinished) {
       return;
     }
     mIsFinished = true;
   }
   mJobScheduler.clearJob();
 }
 @Override
 public void onNewResultImpl(EncodedImage newResult, boolean isLast) {
   if (isLast && !EncodedImage.isValid(newResult)) {
     handleError(new NullPointerException("Encoded image is not valid."));
     return;
   }
   if (!updateDecodeJob(newResult, isLast)) {
     return;
   }
   if (isLast || mProducerContext.isIntermediateResultExpected()) {
     mJobScheduler.scheduleJob();
   }
 }
 /** Updates the decode job. */
 protected boolean updateDecodeJob(EncodedImage ref, boolean isLast) {
   return mJobScheduler.updateJob(ref, isLast);
 }