本文不适合非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,
}
}