您当前的位置: 首页 >  ide

lichong951

暂无认证

  • 3浏览

    0关注

    131博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Glide源码阅读之状态模式[SingleRequest<R>.Status]

lichong951 发布时间:2022-01-14 18:39:46 ,浏览量:3

前言

前面写完策略模式,接着写状态模式;在开始接触这两个模式的时候我也很疑惑,这两个设计模式很相似,用法也很类似。好一段时间我都没有区分这两者的区别。在使用的时候也不知道怎么选择,后来慢慢的加深理解也就总结出规律了。先看看状态模式的经典结构

状态模式介绍

《Android源码设计模式解析与实践》

定义

当一个对象的内在状态改变时容许改变其行为,这个对象开起来像是改变了其类

使用场景 1)一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为 2)代码包含大量与对象状态有关的条件语句, 状态模式将每个条件分支放入一个独立的类中,这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化,这样通过多态来去除过多的、重复的if-else等分支语句

UML类图 在这里插入图片描述

《大话设计模式》

定义: 当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类(和《Android源码设计模式解析与实践》定义相同)

状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化

在这里插入图片描述

策略模式VS状态模式

状态模式和策略模式的结构几乎完全一样,但它们的目的、本质却完全不一样。状态模式的行为是平行的、不可替换的,策略模式的行为是彼此独立、可相互替换的。用一句话来表述,状态模式把对象的行为包装在不同的状态对象里,每个状态对象都有一个共同的抽象状态基类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变–《Android源码设计模式解析与实践》

Glide状态模式应用SingleRequest.Status

包路径:com.bumptech.glide.request.SingleRequest.Status 将资源加载到给定目标中的请求。

/**
 * A {@link Request} that loads a {@link com.bumptech.glide.load.engine.Resource} into a given
 * {@link Target}.
 *
 * @param  The type of the resource that will be transcoded from the loaded resource.
 */
 将资源加载到给定目标中的请求。
public final class SingleRequest implements Request, SizeReadyCallback, ResourceCallback {
  /** Tag for logging internal events, not generally suitable for public use. */
  private static final String TAG = "GlideRequest";
  /** Tag for logging externally useful events (request completion, timing etc). */
  private static final String GLIDE_TAG = "Glide";

  private static final boolean IS_VERBOSE_LOGGABLE = Log.isLoggable(TAG, Log.VERBOSE);
  private int cookie;

  private enum Status {
    /** Created but not yet running. */
    已创建但尚未运行。
    PENDING,
    /** In the process of fetching media. */
    在获取媒体的过程中。
    RUNNING,
    /** Waiting for a callback given to the Target to be called to determine target dimensions. */
    等待给定给Target的回调被调用以确定目标维度。
    WAITING_FOR_SIZE,
    /** Finished loading media successfully. */
    成功加载媒体。
    COMPLETE,
    /** Failed to load media, may be restarted. */
    加载介质失败,可能被重新启动。
    FAILED,
    /** Cleared by the user with a placeholder set, may be restarted. */
    使用占位符设置的用户清除,可能会重新启动。
    CLEARED,
  }

Status.PENDING
 // We are in fact locking on the same lock that will be used for all subsequent method calls.
  @SuppressWarnings("GuardedBy")
  private SingleRequest(
      Context context,
      GlideContext glideContext,
      @NonNull Object requestLock,
      @Nullable Object model,
      Class transcodeClass,
      BaseRequestOptions requestOptions,
      int overrideWidth,
      int overrideHeight,
      Priority priority,
      Target target,
      @Nullable RequestListener targetListener,
      @Nullable List requestListeners,
      RequestCoordinator requestCoordinator,
      Engine engine,
      TransitionFactory animationFactory,
      Executor callbackExecutor) {
    this.requestLock = requestLock;
    this.context = context;
    this.glideContext = glideContext;
    this.model = model;
    this.transcodeClass = transcodeClass;
    this.requestOptions = requestOptions;
 //。。。
    status = Status.PENDING;

   。。。
    }
  }
Status.RUNNING
 @Override
  public void begin() {
    synchronized (requestLock) {
      assertNotCallingCallbacks();
      stateVerifier.throwIfRecycled();
      startTime = LogTime.getLogTime();
     ....

      if (status == Status.RUNNING) {
        throw new IllegalArgumentException("Cannot restart a running request");
      }
      // If we're restarted after we're complete (usually via something like a notifyDataSetChanged
      // that starts an identical request into the same Target or View), we can simply use the
      // resource and size we retrieved the last time around and skip obtaining a new size, starting
      // a new load etc. This does mean that users who want to restart a load because they expect
      // that the view size has changed will need to explicitly clear the View or Target before
      // starting the new load.
      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.

      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());
      }
      if (IS_VERBOSE_LOGGABLE) {
        logV("finished run method in " + LogTime.getElapsedMillis(startTime));
      }
    }
  }
 @Override
  public boolean isRunning() {
    synchronized (requestLock) {
      return status == Status.RUNNING || status == Status.WAITING_FOR_SIZE;
    }
  }
 /** A callback method that should never be invoked directly. */
  @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;

      ...

      // This is a hack that's only useful for testing right now where loads complete synchronously
      // even though under any executor running on any thread but the main thread, the load would
      // have completed asynchronously.
      if (status != Status.RUNNING) {
        loadStatus = null;
      }
      if (IS_VERBOSE_LOGGABLE) {
        logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
      }
    }
Status.WAITING_FOR_SIZE
 @Override
  public void begin() {
    synchronized (requestLock) {
      assertNotCallingCallbacks();
      stateVerifier.throwIfRecycled();
      startTime = LogTime.getLogTime();
      if (model == null) {
        if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
          width = overrideWidth;
          height = overrideHeight;
        }
        // Only log at more verbose log levels if the user has set a fallback drawable, because
        // fallback Drawables indicate the user expects null models occasionally.
        int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
        onLoadFailed(new GlideException("Received null model"), logLevel);
        return;
      }

      if (status == Status.RUNNING) {
        throw new IllegalArgumentException("Cannot restart a running request");
      }

      // If we're restarted after we're complete (usually via something like a notifyDataSetChanged
      // that starts an identical request into the same Target or View), we can simply use the
      // resource and size we retrieved the last time around and skip obtaining a new size, starting
      // a new load etc. This does mean that users who want to restart a load because they expect
      // that the view size has changed will need to explicitly clear the View or Target before
      // starting the new load.
      if (status == Status.COMPLETE) {
        onResourceReady(
 //......
      status = Status.WAITING_FOR_SIZE;
      
     // ....

      if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
          && canNotifyStatusChanged()) {
        target.onLoadStarted(getPlaceholderDrawable());
      }
      if (IS_VERBOSE_LOGGABLE) {
        logV("finished run method in " + LogTime.getElapsedMillis(startTime));
      }
    }
  }
 @Override
  public boolean isRunning() {
    synchronized (requestLock) {
      return status == Status.RUNNING || status == Status.WAITING_FOR_SIZE;
    }
  }
/** A callback method that should never be invoked directly. */
  @Override
  public void onSizeReady(int width, int height) {
    //...
      if (status != Status.WAITING_FOR_SIZE) {
        return;
      }
      status = Status.RUNNING;

     //...

Status.COMPLETE
 public void begin() {
 // If we're restarted after we're complete (usually via something like a notifyDataSetChanged
      // that starts an identical request into the same Target or View), we can simply use the
      // resource and size we retrieved the last time around and skip obtaining a new size, starting
      // a new load etc. This does mean that users who want to restart a load because they expect
      // that the view size has changed will need to explicitly clear the View or Target before
      // starting the new load.
      if (status == Status.COMPLETE) {
        onResourceReady(
            resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
        return;
      }
      
 @Override
  public boolean isComplete() {
    synchronized (requestLock) {
      return status == Status.COMPLETE;
    }
  }
 @Override
  public boolean isAnyResourceSet() {
    synchronized (requestLock) {
      return status == Status.COMPLETE;
    }
  }
/** A callback method that should never be invoked directly. */
  @SuppressWarnings("unchecked")
  @Override
  public void onResourceReady(
  ....
        if (!canSetResource()) {
          toRelease = resource;
          this.resource = null;
          // We can't put the status to complete before asking canSetResource().
          status = Status.COMPLETE;

    ....    
 /**
   * Internal {@link #onResourceReady(Resource, DataSource, boolean)} where arguments are known to
   * be safe.
   *
   * @param resource original {@link Resource}, never null
   * @param result object returned by {@link Resource#get()}, checked for type and never null
   *     
   */
  // We're using experimental APIs...
  @SuppressWarnings({"deprecation", "PMD.UnusedFormalParameter"})
  @GuardedBy("requestLock")
  private void onResourceReady(
      Resource resource, R result, DataSource dataSource, boolean isAlternateCacheKey) {
    // We must call isFirstReadyResource before setting status.
    boolean isFirstResource = isFirstReadyResource();

    status = Status.COMPLETE;

    this.resource = resource;
...
Status.FAILED

  private void onLoadFailed(GlideException e, int maxLogLevel) {
    stateVerifier.throwIfRecycled();
    synchronized (requestLock) {
      e.setOrigin(requestOrigin);
      int logLevel = glideContext.getLogLevel();
      if (logLevel             
关注
打赏
1659512212
查看更多评论
0.1169s