@Override
  public void run() {
    AtomicBoolean pause = engine.getPause();
    if (pause.get()) {
      synchronized (pause) {
        log(LOG_WAITING_FOR_RESUME, memoryCacheKey);
        try {
          pause.wait();
        } catch (InterruptedException e) {
          L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
          return;
        }
        log(LOG_RESUME_AFTER_PAUSE, memoryCacheKey);
      }
    }
    if (checkTaskIsNotActual()) return;

    if (options.shouldDelayBeforeLoading()) {
      log(LOG_DELAY_BEFORE_LOADING, options.getDelayBeforeLoading(), memoryCacheKey);
      try {
        Thread.sleep(options.getDelayBeforeLoading());
      } catch (InterruptedException e) {
        L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
        return;
      }

      if (checkTaskIsNotActual()) return;
    }

    ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;
    log(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey);
    if (loadFromUriLock.isLocked()) {
      log(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey);
    }

    loadFromUriLock.lock();
    Bitmap bmp;
    try {
      if (checkTaskIsNotActual()) return;

      bmp = configuration.memoryCache.get(memoryCacheKey);
      if (bmp == null) {
        bmp = tryLoadBitmap();
        if (bmp == null) return;

        if (checkTaskIsNotActual() || checkTaskIsInterrupted()) return;

        if (options.isCacheInMemory()) {
          if (options.shouldPreProcess()) {
            log(LOG_PREPROCESS_IMAGE, memoryCacheKey);
            bmp = options.getPreProcessor().process(bmp, imageView);
          }

          log(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey);
          configuration.memoryCache.put(memoryCacheKey, bmp);
        }
      } else {
        log(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING, memoryCacheKey);
      }

      if (options.shouldPostProcess()) {
        log(LOG_POSTPROCESS_IMAGE, memoryCacheKey);
        bmp = options.getPostProcessor().process(bmp, imageView);
      }
    } finally {
      loadFromUriLock.unlock();
    }

    if (checkTaskIsNotActual() || checkTaskIsInterrupted()) return;

    DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine);
    displayBitmapTask.setLoggingEnabled(loggingEnabled);
    handler.post(displayBitmapTask);
  }
  @Override
  public void run() {
    if (waitIfPaused()) return;
    if (delayIfNeed()) return;

    ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;
    log(LOG_START_DISPLAY_IMAGE_TASK);
    if (loadFromUriLock.isLocked()) {
      log(LOG_WAITING_FOR_IMAGE_LOADED);
    }

    loadFromUriLock.lock();
    Bitmap bmp;
    try {
      checkTaskNotActual();

      bmp = configuration.memoryCache.get(memoryCacheKey);
      if (bmp == null) {
        bmp = tryLoadBitmap();
        if (bmp == null) return; // listener callback already was fired

        checkTaskNotActual();
        checkTaskInterrupted();

        if (options.shouldPreProcess()) {
          log(LOG_PREPROCESS_IMAGE);
          bmp = options.getPreProcessor().process(bmp);
          if (bmp == null) {
            L.e(ERROR_PRE_PROCESSOR_NULL, memoryCacheKey);
          }
        }

        if (bmp != null && options.isCacheInMemory()) {
          log(LOG_CACHE_IMAGE_IN_MEMORY);
          configuration.memoryCache.put(memoryCacheKey, bmp);
        }
      } else {
        loadedFrom = LoadedFrom.MEMORY_CACHE;
        log(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING);
      }

      if (bmp != null && options.shouldPostProcess()) {
        log(LOG_POSTPROCESS_IMAGE);
        bmp = options.getPostProcessor().process(bmp);
        if (bmp == null) {
          L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey);
        }
      }
      checkTaskNotActual();
      checkTaskInterrupted();
    } catch (TaskCancelledException e) {
      fireCancelEvent();
      return;
    } finally {
      loadFromUriLock.unlock();
    }

    DisplayBitmapTask displayBitmapTask =
        new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
    displayBitmapTask.setLoggingEnabled(writeLogs);
    if (options.isSyncLoading()) {
      displayBitmapTask.run();
    } else {
      handler.post(displayBitmapTask);
    }
  }