Skip to content

Commit b06b0cc

Browse files
committed
Add API to start a new Request when a load fails.
We’re going with .error() to match the behavior of the .error Drawable and resource id methods. fallback() is a somewhat better name, but we unfortunately already use it to handle cases where models are null and expected to be null. Fixes #451.
1 parent a136852 commit b06b0cc

File tree

5 files changed

+249
-11
lines changed

5 files changed

+249
-11
lines changed

library/src/main/java/com/bumptech/glide/RequestBuilder.java

+78-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import android.support.annotation.Nullable;
99
import android.widget.ImageView;
1010
import com.bumptech.glide.load.engine.DiskCacheStrategy;
11+
import com.bumptech.glide.request.ErrorRequestCoordinator;
1112
import com.bumptech.glide.request.FutureTarget;
1213
import com.bumptech.glide.request.Request;
1314
import com.bumptech.glide.request.RequestCoordinator;
@@ -55,6 +56,7 @@ public class RequestBuilder<TranscodeType> implements Cloneable {
5556
// than relying on model not to be null.
5657
@Nullable private RequestListener<TranscodeType> requestListener;
5758
@Nullable private RequestBuilder<TranscodeType> thumbnailBuilder;
59+
@Nullable private RequestBuilder<TranscodeType> errorBuilder;
5860
@Nullable private Float thumbSizeMultiplier;
5961
private boolean isDefaultTransitionOptionsSet = true;
6062
private boolean isModelSet;
@@ -115,7 +117,7 @@ public RequestBuilder<TranscodeType> transition(
115117
}
116118

117119
/**
118-
* Sets a RequestBuilder listener to monitor the resource load. It's best to create a single
120+
* Sets a {@link RequestListener} to monitor the resource load. It's best to create a single
119121
* instance of an exception handler per type of request (usually activity/fragment) rather than
120122
* pass one in per request to avoid some redundant object allocation.
121123
*
@@ -131,6 +133,34 @@ public RequestBuilder<TranscodeType> listener(
131133
return this;
132134
}
133135

136+
/**
137+
* Sets a {@link RequestBuilder} that is built and run iff the load started by this
138+
* {@link RequestBuilder} fails.
139+
*
140+
* <p>If this {@link RequestBuilder} uses a thumbnail that succeeds the given error
141+
* {@link RequestBuilder} will be started anyway if the non-thumbnail request fails.
142+
*
143+
* <p>Recursive calls to {@link #error(RequestBuilder)} as well as calls to
144+
* {@link #thumbnail(float)} and {@link #thumbnail(RequestBuilder)} are supported for the given
145+
* error {@link RequestBuilder}.
146+
*
147+
* <p>Unlike {@link #thumbnail(RequestBuilder)} and {@link #thumbnail(float)}, no options from
148+
* this primary {@link RequestBuilder} are propagated to the given error {@link RequestBuilder}.
149+
* Options like priority, override widths and heights and transitions must be applied
150+
* independently to the error builder.
151+
*
152+
* <p>The given {@link RequestBuilder} will start and potentially override a fallback drawable
153+
* if it's set on this {@link RequestBuilder} via
154+
* {@link RequestOptions#fallback(android.graphics.drawable.Drawable)} or
155+
* {@link RequestOptions#fallback(int)}.
156+
*
157+
* @return This {@link RequestBuilder}.
158+
*/
159+
public RequestBuilder<TranscodeType> error(@Nullable RequestBuilder<TranscodeType> errorBuilder) {
160+
this.errorBuilder = errorBuilder;
161+
return this;
162+
}
163+
134164
/**
135165
* Loads and displays the resource retrieved by the given thumbnail request if it finishes before
136166
* this request. Best used for loading thumbnail resources that are smaller and will be loaded
@@ -607,9 +637,55 @@ private Request buildRequest(Target<TranscodeType> target, RequestOptions reques
607637
}
608638

609639
private Request buildRequestRecursive(Target<TranscodeType> target,
610-
@Nullable ThumbnailRequestCoordinator parentCoordinator,
640+
@Nullable RequestCoordinator parentCoordinator,
611641
TransitionOptions<?, ? super TranscodeType> transitionOptions,
612642
Priority priority, int overrideWidth, int overrideHeight, RequestOptions requestOptions) {
643+
644+
// Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
645+
ErrorRequestCoordinator errorRequestCoordinator = null;
646+
if (errorBuilder != null) {
647+
errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
648+
parentCoordinator = errorRequestCoordinator;
649+
}
650+
651+
Request mainRequest =
652+
buildThumbnailRequestRecursive(
653+
target,
654+
parentCoordinator,
655+
transitionOptions,
656+
priority,
657+
overrideWidth,
658+
overrideHeight,
659+
requestOptions);
660+
661+
if (errorRequestCoordinator == null) {
662+
return mainRequest;
663+
}
664+
665+
int errorOverrideWidth = errorBuilder.requestOptions.getOverrideWidth();
666+
int errorOverrideHeight = errorBuilder.requestOptions.getOverrideHeight();
667+
if (Util.isValidDimensions(overrideWidth, overrideHeight)
668+
&& !errorBuilder.requestOptions.isValidOverride()) {
669+
errorOverrideWidth = requestOptions.getOverrideWidth();
670+
errorOverrideHeight = requestOptions.getOverrideHeight();
671+
}
672+
673+
Request errorRequest = errorBuilder.buildRequestRecursive(
674+
target,
675+
errorRequestCoordinator,
676+
errorBuilder.transitionOptions,
677+
errorBuilder.requestOptions.getPriority(),
678+
errorOverrideWidth,
679+
errorOverrideHeight,
680+
errorBuilder.requestOptions);
681+
errorRequestCoordinator.setRequests(mainRequest, errorRequest);
682+
return errorRequestCoordinator;
683+
}
684+
685+
private Request buildThumbnailRequestRecursive(Target<TranscodeType> target,
686+
@Nullable RequestCoordinator parentCoordinator,
687+
TransitionOptions<?, ? super TranscodeType> transitionOptions,
688+
Priority priority, int overrideWidth, int overrideHeight, RequestOptions requestOptions) {
613689
if (thumbnailBuilder != null) {
614690
// Recursive case: contains a potentially recursive thumbnail request builder.
615691
if (isThumbnailBuilt) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package com.bumptech.glide.request;
2+
3+
/**
4+
* Runs a single primary {@link Request} until it completes and then a fallback error request only
5+
* if the single primary request fails.
6+
*/
7+
public final class ErrorRequestCoordinator implements RequestCoordinator,
8+
Request {
9+
10+
private final RequestCoordinator coordinator;
11+
private Request primary;
12+
private Request error;
13+
14+
public ErrorRequestCoordinator(RequestCoordinator coordinator) {
15+
this.coordinator = coordinator;
16+
}
17+
18+
public void setRequests(Request primary, Request error) {
19+
this.primary = primary;
20+
this.error = error;
21+
}
22+
23+
@Override
24+
public void begin() {
25+
if (!primary.isRunning()) {
26+
primary.begin();
27+
}
28+
}
29+
30+
@Override
31+
public void pause() {
32+
if (!primary.isFailed()) {
33+
primary.pause();
34+
}
35+
if (error.isRunning()) {
36+
error.pause();
37+
}
38+
}
39+
40+
@Override
41+
public void clear() {
42+
if (primary.isFailed()) {
43+
error.clear();
44+
} else {
45+
primary.clear();
46+
}
47+
}
48+
49+
@Override
50+
public boolean isPaused() {
51+
return primary.isFailed() ? error.isPaused() : primary.isPaused();
52+
}
53+
54+
@Override
55+
public boolean isRunning() {
56+
return primary.isFailed() ? error.isRunning() : primary.isRunning();
57+
}
58+
59+
@Override
60+
public boolean isComplete() {
61+
return primary.isFailed() ? error.isComplete() : primary.isComplete();
62+
}
63+
64+
@Override
65+
public boolean isResourceSet() {
66+
return primary.isFailed() ? error.isResourceSet() : primary.isResourceSet();
67+
}
68+
69+
@Override
70+
public boolean isCancelled() {
71+
return primary.isFailed() ? error.isCancelled() : primary.isCancelled();
72+
}
73+
74+
@Override
75+
public boolean isFailed() {
76+
return primary.isFailed() && error.isFailed();
77+
}
78+
79+
@Override
80+
public void recycle() {
81+
primary.recycle();
82+
error.recycle();
83+
}
84+
85+
@Override
86+
public boolean isEquivalentTo(Request o) {
87+
if (o instanceof ErrorRequestCoordinator) {
88+
ErrorRequestCoordinator other = (ErrorRequestCoordinator) o;
89+
return primary.isEquivalentTo(other.primary) && error.isEquivalentTo(other.error);
90+
}
91+
return false;
92+
}
93+
94+
@Override
95+
public boolean canSetImage(Request request) {
96+
return parentCanSetImage() && isValidRequest(request);
97+
}
98+
99+
private boolean parentCanSetImage() {
100+
return coordinator == null || coordinator.canSetImage(this);
101+
}
102+
103+
@Override
104+
public boolean canNotifyStatusChanged(Request request) {
105+
return parentCanNotifyStatusChanged() && isValidRequest(request);
106+
}
107+
108+
private boolean parentCanNotifyStatusChanged() {
109+
return coordinator == null || coordinator.canNotifyStatusChanged(this);
110+
}
111+
112+
private boolean isValidRequest(Request request) {
113+
return request.equals(primary) || (primary.isFailed() && request.equals(error));
114+
}
115+
116+
@Override
117+
public boolean isAnyResourceSet() {
118+
return parentIsAnyResourceSet() || isResourceSet();
119+
}
120+
121+
private boolean parentIsAnyResourceSet() {
122+
return coordinator != null && coordinator.isAnyResourceSet();
123+
}
124+
125+
@Override
126+
public void onRequestSuccess(Request request) {
127+
if (coordinator != null) {
128+
coordinator.onRequestSuccess(this);
129+
}
130+
}
131+
132+
@Override
133+
public void onRequestFailed(Request request) {
134+
if (!request.equals(error)) {
135+
if (!error.isRunning()) {
136+
error.begin();
137+
}
138+
return;
139+
}
140+
141+
if (coordinator != null) {
142+
coordinator.onRequestFailed(error);
143+
}
144+
}
145+
}

library/src/main/java/com/bumptech/glide/request/RequestCoordinator.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ public interface RequestCoordinator {
2828
boolean isAnyResourceSet();
2929

3030
/**
31-
* Must be called when a request coordinated by this object completes successfully.
31+
* Must be called when a {@link Request} coordinated by this object completes successfully.
3232
*/
3333
void onRequestSuccess(Request request);
34+
35+
/** Must be called when a {@link Request} coordinated by this object fails. */
36+
void onRequestFailed(Request request);
3437
}

library/src/main/java/com/bumptech/glide/request/SingleRequest.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,9 @@ void cancel() {
277277
private void assertNotCallingCallbacks() {
278278
if (isCallingCallbacks) {
279279
throw new IllegalStateException("You can't start or clear loads in RequestListener or"
280-
+ " Target callbacks. If you must do so, consider posting your into() or clear() calls"
281-
+ " to the main thread using a Handler instead.");
280+
+ " Target callbacks. If you're trying to start a fallback request when a load fails, use"
281+
+ " RequestBuilder#error(RequestBuilder). Otherwise consider posting your into() or"
282+
+ " clear() calls to the main thread using a Handler instead.");
282283
}
283284
}
284285

@@ -492,6 +493,12 @@ private void notifyLoadSuccess() {
492493
}
493494
}
494495

496+
private void notifyLoadFailed() {
497+
if (requestCoordinator != null) {
498+
requestCoordinator.onRequestFailed(this);
499+
}
500+
}
501+
495502
/**
496503
* A callback method that should never be invoked directly.
497504
*/
@@ -595,6 +602,8 @@ private void onLoadFailed(GlideException e, int maxLogLevel) {
595602
} finally {
596603
isCallingCallbacks = false;
597604
}
605+
606+
notifyLoadFailed();
598607
}
599608

600609
@Override

library/src/main/java/com/bumptech/glide/request/ThumbnailRequestCoordinator.java

+11-6
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,17 @@ public void onRequestSuccess(Request request) {
7777
}
7878
}
7979

80+
@Override
81+
public void onRequestFailed(Request request) {
82+
if (!request.equals(full)) {
83+
return;
84+
}
85+
86+
if (coordinator != null) {
87+
coordinator.onRequestFailed(this);
88+
}
89+
}
90+
8091
private boolean parentIsAnyResourceSet() {
8192
return coordinator != null && coordinator.isAnyResourceSet();
8293
}
@@ -102,9 +113,6 @@ public void pause() {
102113
thumb.pause();
103114
}
104115

105-
/**
106-
* {@inheritDoc}
107-
*/
108116
@Override
109117
public void clear() {
110118
isRunning = false;
@@ -151,9 +159,6 @@ public boolean isFailed() {
151159
return full.isFailed();
152160
}
153161

154-
/**
155-
* {@inheritDoc}.
156-
*/
157162
@Override
158163
public void recycle() {
159164
full.recycle();

0 commit comments

Comments
 (0)