0
点赞
收藏
分享

微信扫一扫

【设计模式】策略模式——策略模式在Glide源码中的应用

本文不适合非Android开发者阅读,并且本文有点儿长,Android开发者阅读也请谨慎。


策略模式在Glide源码中有两个比较典型的应用场景,两个抽象策略类分别是DiskCacheStrategy和DownsampleStrategy。


DiskCacheStrategy

DiskCacheStrategy是一组可用的媒体缓存策略,代码和五个具体策略子类的代码如下:

public abstract class DiskCacheStrategy {

  /**
   * 既缓存原始图片,也缓存转换过后的图片
   */
  public static final DiskCacheStrategy ALL =
      new DiskCacheStrategy() {
        @Override
        public boolean isDataCacheable(DataSource dataSource) {
          return dataSource == DataSource.REMOTE;
        }

        @Override
        public boolean isResourceCacheable(
            boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
          return dataSource != DataSource.RESOURCE_DISK_CACHE
              && dataSource != DataSource.MEMORY_CACHE;
        }

        @Override
        public boolean decodeCachedResource() {
          return true;
        }

        @Override
        public boolean decodeCachedData() {
          return true;
        }
      };

  /** 不缓存数据 */
  public static final DiskCacheStrategy NONE =
      new DiskCacheStrategy() {
        @Override
        public boolean isDataCacheable(DataSource dataSource) {
          return false;
        }

        @Override
        public boolean isResourceCacheable(
            boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
          return false;
        }

        @Override
        public boolean decodeCachedResource() {
          return false;
        }

        @Override
        public boolean decodeCachedData() {
          return false;
        }
      };

  /** 只缓存原始图片 */
  public static final DiskCacheStrategy DATA =
      new DiskCacheStrategy() {
        @Override
        public boolean isDataCacheable(DataSource dataSource) {
          return dataSource != DataSource.DATA_DISK_CACHE && dataSource != DataSource.MEMORY_CACHE;
        }

        @Override
        public boolean isResourceCacheable(
            boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
          return false;
        }

        @Override
        public boolean decodeCachedResource() {
          return false;
        }

        @Override
        public boolean decodeCachedData() {
          return true;
        }
      };

  /** 只缓存转换过后的图片 */
  public static final DiskCacheStrategy RESOURCE =
      new DiskCacheStrategy() {
        @Override
        public boolean isDataCacheable(DataSource dataSource) {
          return false;
        }

        @Override
        public boolean isResourceCacheable(
            boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
          return dataSource != DataSource.RESOURCE_DISK_CACHE
              && dataSource != DataSource.MEMORY_CACHE;
        }

        @Override
        public boolean decodeCachedResource() {
          return true;
        }

        @Override
        public boolean decodeCachedData() {
          return false;
        }
      };

  /**
   * 让Glide根据图片资源智能地选择使用以上哪一种缓存策略(默认策略)
   */
  public static final DiskCacheStrategy AUTOMATIC =
      new DiskCacheStrategy() {
        @Override
        public boolean isDataCacheable(DataSource dataSource) {
          return dataSource == DataSource.REMOTE;
        }

        @Override
        public boolean isResourceCacheable(
            boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
          return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)
                  || dataSource == DataSource.LOCAL)
              && encodeStrategy == EncodeStrategy.TRANSFORMED;
        }

        @Override
        public boolean decodeCachedResource() {
          return true;
        }

        @Override
        public boolean decodeCachedData() {
          return true;
        }
      };

  /**
   * Returns true if this request should cache the original unmodified data.
   */
  public abstract boolean isDataCacheable(DataSource dataSource);

  /**
   * Returns true if this request should cache the final transformed resource.
   */
  public abstract boolean isResourceCacheable(
      boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy);

  /** Returns true if this request should attempt to decode cached resource data. */
  public abstract boolean decodeCachedResource();

  /** Returns true if this request should attempt to decode cached source data. */
  public abstract boolean decodeCachedData();
}


DownsampleStrategy

DownsampleStrategy是对图像进行下采样时使用的算法的抽象策略类,下采样是缩小图像体积的一种方式。因为Glide是针对Android的框架,所以Glide的下采样策略和Android的ImageView的scaleType的策略基本一致。代码如下:

public abstract class DownsampleStrategy {

  public static final DownsampleStrategy AT_LEAST = new AtLeast();

  public static final DownsampleStrategy AT_MOST = new AtMost();

  
  public static final DownsampleStrategy FIT_CENTER = new FitCenter();

  public static final DownsampleStrategy CENTER_INSIDE = new CenterInside();

  public static final DownsampleStrategy CENTER_OUTSIDE = new CenterOutside();

  public static final DownsampleStrategy NONE = new None();

  public static final DownsampleStrategy DEFAULT = CENTER_OUTSIDE;

  public static final Option<DownsampleStrategy> OPTION =
      Option.memory(
          "com.bumptech.glide.load.resource.bitmap.Downsampler.DownsampleStrategy", DEFAULT);

  @Synthetic
  static final boolean IS_BITMAP_FACTORY_SCALING_SUPPORTED =
      Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;


  public abstract float getScaleFactor(
      int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight);

  public abstract SampleSizeRounding getSampleSizeRounding(
      int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight);

  private static class FitCenter extends DownsampleStrategy {

    @Synthetic
    FitCenter() {}

    @Override
    public float getScaleFactor(
        int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
      if (IS_BITMAP_FACTORY_SCALING_SUPPORTED) {
        float widthPercentage = requestedWidth / (float) sourceWidth;
        float heightPercentage = requestedHeight / (float) sourceHeight;

        return Math.min(widthPercentage, heightPercentage);
      } else {
        int maxIntegerFactor =
            Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth);
        return maxIntegerFactor == 0 ? 1f : 1f / Integer.highestOneBit(maxIntegerFactor);
      }
    }

    @Override
    public SampleSizeRounding getSampleSizeRounding(
        int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
      if (IS_BITMAP_FACTORY_SCALING_SUPPORTED) {
        return SampleSizeRounding.QUALITY;
      } else {
        return SampleSizeRounding.MEMORY;
      }
    }
  }

  private static class CenterOutside extends DownsampleStrategy {

    @Synthetic
    CenterOutside() {}

    @Override
    public float getScaleFactor(
        int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
      float widthPercentage = requestedWidth / (float) sourceWidth;
      float heightPercentage = requestedHeight / (float) sourceHeight;
      return Math.max(widthPercentage, heightPercentage);
    }

    @Override
    public SampleSizeRounding getSampleSizeRounding(
        int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
      return SampleSizeRounding.QUALITY;
    }
  }

  private static class AtLeast extends DownsampleStrategy {

    @Synthetic
    AtLeast() {}

    @Override
    public float getScaleFactor(
        int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
      int minIntegerFactor = Math.min(sourceHeight / requestedHeight, sourceWidth / requestedWidth);
      return minIntegerFactor == 0 ? 1f : 1f / Integer.highestOneBit(minIntegerFactor);
    }

    @Override
    public SampleSizeRounding getSampleSizeRounding(
        int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
      return SampleSizeRounding.QUALITY;
    }
  }

  private static class AtMost extends DownsampleStrategy {

    @Synthetic
    AtMost() {}

    @Override
    public float getScaleFactor(
        int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
      int maxIntegerFactor =
          (int)
              Math.ceil(
                  Math.max(
                      sourceHeight / (float) requestedHeight,
                      sourceWidth / (float) requestedWidth));
      int lesserOrEqualSampleSize = Math.max(1, Integer.highestOneBit(maxIntegerFactor));
      int greaterOrEqualSampleSize =
          lesserOrEqualSampleSize << (lesserOrEqualSampleSize < maxIntegerFactor ? 1 : 0);
      return 1f / greaterOrEqualSampleSize;
    }

    @Override
    public SampleSizeRounding getSampleSizeRounding(
        int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
      return SampleSizeRounding.MEMORY;
    }
  }

  private static class None extends DownsampleStrategy {

    @Synthetic
    None() {}

    @Override
    public float getScaleFactor(
        int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
      return 1f;
    }

    @Override
    public SampleSizeRounding getSampleSizeRounding(
        int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
      return SampleSizeRounding.QUALITY;
    }
  }

  private static class CenterInside extends DownsampleStrategy {

    @Synthetic
    CenterInside() {}

    @Override
    public float getScaleFactor(
        int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {

      return Math.min(
          1.f,
          FIT_CENTER.getScaleFactor(sourceWidth, sourceHeight, requestedWidth, requestedHeight));
    }

    @Override
    public SampleSizeRounding getSampleSizeRounding(
        int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
      return getScaleFactor(sourceWidth, sourceHeight, requestedWidth, requestedHeight) == 1.f
          ? SampleSizeRounding.QUALITY
          : FIT_CENTER.getSampleSizeRounding(
              sourceWidth, sourceHeight, requestedWidth, requestedHeight);
    }
  }

  /**
   * 有损压缩时的对内存占用和图片质量的取舍倾向
   */
  public enum SampleSizeRounding {
    /**
     * 省内存,即会更倾向于将图像向下采样至小于目标大小,以使用更少的内存。
     */
    MEMORY,
    /**
     * 保质量,即会更倾向于将图像向下采样至大于目标大小,以保持图像的质量,会牺牲额外的内存使用。
     */
    QUALITY,
  }
}



举报

相关推荐

0 条评论