状态模式在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明显减少,代码质量明显提高。