0
点赞
收藏
分享

微信扫一扫

【设计模式】状态模式——状态模式在Glide源码中的应用

Jonescy 2024-01-16 阅读 13

状态模式在Glide代码中的应用比较广泛,本文只分析最典型的两个


DecodeJob.Stage

当Glide在活动资源、内存缓存中找不到需要的数据时,就会开启一个线程从磁盘缓存或者网络/本地原始数据中进行加载,这个线程就是DecodeJob。而DecodeJob.Stage是Glide的DecodeJob类的一个枚举子类,是状态模式在Glide源码里的典型应用场景之一。代码如下:

class DecodeJob<R>
    implements DataFetcherGenerator.FetcherReadyCallback,
        Runnable,
        Comparable<DecodeJob<?>>,
        Poolable {

  // ……代码省略……

  @SuppressWarnings("PMD.AvoidRethrowingException")
  @Override
  public void run() {
    // ……代码省略……
    try {
      if (isCancelled) {
        notifyFailed();
        return;
      }
      runWrapped();
    } 
    } catch (Throwable t) {
      // ……代码省略……
    }
  }

  private void runWrapped() {
    switch (runReason) {
      case INITIALIZE:
        stage = getNextStage(Stage.INITIALIZE);
        currentGenerator = getNextGenerator();
        runGenerators();
        break;
      case SWITCH_TO_SOURCE_SERVICE:
        runGenerators();
        break;
      case DECODE_DATA:
        decodeFromRetrievedData();
        break;
      default:
        throw new IllegalStateException("Unrecognized run reason: " + runReason);
    }
  }

  private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      case RESOURCE_CACHE:
        return new ResourceCacheGenerator(decodeHelper, this);
      case DATA_CACHE:
        return new DataCacheGenerator(decodeHelper, this);
      case SOURCE:
        return new SourceGenerator(decodeHelper, this);
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }

  private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    while (!isCancelled
        && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();

      if (stage == Stage.SOURCE) {
        reschedule(RunReason.SWITCH_TO_SOURCE_SERVICE);
        return;
      }
    }
    // We've run out of stages and generators, give up.
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
      notifyFailed();
    }

    // Otherwise a generator started a new load and we expect to be called back in
    // onDataFetcherReady.
  }

  private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE:
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE
            : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE
            : getNextStage(Stage.DATA_CACHE);
      case DATA_CACHE:
        // Skip loading from source if the user opted to only retrieve the resource from cache.
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
  }

  //……代码省略……

  private enum Stage {
    /** 初始状态 */
    INITIALIZE,
    /** 从缓存的处理后的数据解码 */
    RESOURCE_CACHE,
    /** 从缓存的未处理的数据解码 */
    DATA_CACHE,
    /** 从网络(或本地)原始数据解码 */
    SOURCE,
    /** 对资源进行编码 */
    ENCODE,
    /** 结束状态 */
    FINISHED,
  }
}

可见状态切换的逻辑集中在getNextStage()方法,配合我们的磁盘缓存策略,决定了每个状态对应的下一个状态是什么


SingleRequest.Status

SingleRequest是用来将资源加载到给定目标中的请求,SingleRequest.Status也是状态模式在Glide源码里的典型应用场景之一。

代码如下:

public final class SingleRequest<R> implements Request, SizeReadyCallback, ResourceCallback {

  private enum Status {
    /** 已创建但尚未运行 */
    PENDING,
    /** 在获取媒体的过程中 */
    RUNNING,
    /** 等待给定给Target的回调被调用以确定目标维度 */
    WAITING_FOR_SIZE,
    /** 成功加载媒体 */
    COMPLETE,
    /** 加载媒体失败,可能会被重新启动 */
    FAILED,
    /** 被使用占位符(placeholder)的用户清除,可能会被重新启动 */
    CLEARED,
  }
  
  @GuardedBy("requestLock")
  private Status status;

  @Override
  public void begin() {
    synchronized (requestLock) {
      // ……代码省略……

      if (status == Status.RUNNING) {
        throw new IllegalArgumentException("Cannot restart a running request");
      }
      
      if (status == Status.COMPLETE) {
        onResourceReady(
            resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
        return;
      }

      // Restarts for requests that are neither complete nor running can be treated as new requests
      // and can run again from the beginning.

      experimentalNotifyRequestStarted(model);

      cookie = GlideTrace.beginSectionAsync(TAG);
      status = Status.WAITING_FOR_SIZE;
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        onSizeReady(overrideWidth, overrideHeight);
      } else {
        target.getSize(this);
      }

      if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
          && canNotifyStatusChanged()) {
        target.onLoadStarted(getPlaceholderDrawable());
      }
      // ……代码省略……
    }
  }

  @Override
  public void clear() {
    Resource<R> toRelease = null;
    synchronized (requestLock) {
      assertNotCallingCallbacks();
      stateVerifier.throwIfRecycled();
      if (status == Status.CLEARED) {
        return;
      }
      cancel();
      // Resource must be released before canNotifyStatusChanged is called.
      if (resource != null) {
        toRelease = resource;
        resource = null;
      }
      if (canNotifyCleared()) {
        target.onLoadCleared(getPlaceholderDrawable());
      }

      GlideTrace.endSectionAsync(TAG, cookie);
      status = Status.CLEARED;
    }

    if (toRelease != null) {
      engine.release(toRelease);
    }
  }
  
  // ……代码省略……
  
  @Override
  public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    synchronized (requestLock) {
      if (IS_VERBOSE_LOGGABLE) {
        logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
      }
      if (status != Status.WAITING_FOR_SIZE) {
        return;
      }
      status = Status.RUNNING;

      // ……代码省略……
    }
  }
  
}

从begin()方法的代码中可以看到SingleRequest的行为是被SingleRequest的状态()决定,外部调用SingleRequest暴露的public方法(比如clear())并不直接影响SingleRequest的行为,而是通过改变状态,控制SingleRequest行为的改变。这种写法有助于解决过复杂的if-else业务重构,类比同一应用场景下bug明显减少,代码质量明显提高。



举报

相关推荐

0 条评论