Skip to content

Commit 9328999

Browse files
adrianvsjudd
adrianv
authored andcommitted
Support multiple listeners in a Glide request.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=191378721
1 parent a683e04 commit 9328999

File tree

4 files changed

+176
-73
lines changed

4 files changed

+176
-73
lines changed

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

+27-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import com.bumptech.glide.util.Util;
3535
import java.io.File;
3636
import java.net.URL;
37+
import java.util.ArrayList;
38+
import java.util.List;
3739

3840
/**
3941
* A generic class that can handle setting options and staring loads for generic resource types.
@@ -66,7 +68,7 @@ public class RequestBuilder<TranscodeType> implements Cloneable,
6668
@Nullable private Object model;
6769
// model may occasionally be null, so to enforce that load() was called, put a boolean rather
6870
// than relying on model not to be null.
69-
@Nullable private RequestListener<TranscodeType> requestListener;
71+
@Nullable private List<RequestListener<TranscodeType>> requestListeners;
7072
@Nullable private RequestBuilder<TranscodeType> thumbnailBuilder;
7173
@Nullable private RequestBuilder<TranscodeType> errorBuilder;
7274
@Nullable private Float thumbSizeMultiplier;
@@ -144,6 +146,9 @@ public RequestBuilder<TranscodeType> transition(
144146
* instance of an exception handler per type of request (usually activity/fragment) rather than
145147
* pass one in per request to avoid some redundant object allocation.
146148
*
149+
* <p>Subsequent calls to this method will replace previously set listeners. To set multiple
150+
* listeners, use {@link #addListener} instead.
151+
*
147152
* @param requestListener The request listener to use.
148153
* @return This request builder.
149154
*/
@@ -152,8 +157,27 @@ public RequestBuilder<TranscodeType> transition(
152157
@SuppressWarnings("unchecked")
153158
public RequestBuilder<TranscodeType> listener(
154159
@Nullable RequestListener<TranscodeType> requestListener) {
155-
this.requestListener = requestListener;
160+
this.requestListeners = null;
161+
return addListener(requestListener);
162+
}
156163

164+
/**
165+
* Adds a {@link RequestListener}. If called multiple times, all passed
166+
* {@link RequestListener listeners} will be called in order.
167+
*
168+
* @param requestListener The request listener to use. If {@code null}, this method is a noop.
169+
* @return This request builder.
170+
*/
171+
@NonNull
172+
@CheckResult
173+
public RequestBuilder<TranscodeType> addListener(
174+
@Nullable RequestListener<TranscodeType> requestListener) {
175+
if (requestListener != null) {
176+
if (this.requestListeners == null) {
177+
this.requestListeners = new ArrayList<>();
178+
}
179+
this.requestListeners.add(requestListener);
180+
}
157181
return this;
158182
}
159183

@@ -1041,7 +1065,7 @@ private Request obtainRequest(
10411065
priority,
10421066
target,
10431067
targetListener,
1044-
requestListener,
1068+
requestListeners,
10451069
requestCoordinator,
10461070
glideContext.getEngine(),
10471071
transitionOptions.getTransitionFactory());

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

+40-18
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.bumptech.glide.util.Util;
2525
import com.bumptech.glide.util.pool.FactoryPools;
2626
import com.bumptech.glide.util.pool.StateVerifier;
27+
import java.util.List;
2728

2829
/**
2930
* A {@link Request} that loads a {@link com.bumptech.glide.load.engine.Resource} into a given
@@ -103,7 +104,7 @@ private enum Status {
103104
private int overrideHeight;
104105
private Priority priority;
105106
private Target<R> target;
106-
private RequestListener<R> requestListener;
107+
@Nullable private List<RequestListener<R>> requestListeners;
107108
private Engine engine;
108109
private TransitionFactory<? super R> animationFactory;
109110
private Resource<R> resource;
@@ -127,7 +128,7 @@ public static <R> SingleRequest<R> obtain(
127128
Priority priority,
128129
Target<R> target,
129130
RequestListener<R> targetListener,
130-
RequestListener<R> requestListener,
131+
@Nullable List<RequestListener<R>> requestListeners,
131132
RequestCoordinator requestCoordinator,
132133
Engine engine,
133134
TransitionFactory<? super R> animationFactory) {
@@ -147,7 +148,7 @@ public static <R> SingleRequest<R> obtain(
147148
priority,
148149
target,
149150
targetListener,
150-
requestListener,
151+
requestListeners,
151152
requestCoordinator,
152153
engine,
153154
animationFactory);
@@ -171,7 +172,7 @@ private void init(
171172
Priority priority,
172173
Target<R> target,
173174
RequestListener<R> targetListener,
174-
RequestListener<R> requestListener,
175+
@Nullable List<RequestListener<R>> requestListeners,
175176
RequestCoordinator requestCoordinator,
176177
Engine engine,
177178
TransitionFactory<? super R> animationFactory) {
@@ -185,7 +186,7 @@ private void init(
185186
this.priority = priority;
186187
this.target = target;
187188
this.targetListener = targetListener;
188-
this.requestListener = requestListener;
189+
this.requestListeners = requestListeners;
189190
this.requestCoordinator = requestCoordinator;
190191
this.engine = engine;
191192
this.animationFactory = animationFactory;
@@ -209,7 +210,7 @@ public void recycle() {
209210
overrideWidth = -1;
210211
overrideHeight = -1;
211212
target = null;
212-
requestListener = null;
213+
requestListeners = null;
213214
targetListener = null;
214215
requestCoordinator = null;
215216
animationFactory = null;
@@ -570,10 +571,18 @@ private void onResourceReady(Resource<R> resource, R result, DataSource dataSour
570571

571572
isCallingCallbacks = true;
572573
try {
573-
if ((requestListener == null
574-
|| !requestListener.onResourceReady(result, model, target, dataSource, isFirstResource))
575-
&& (targetListener == null
576-
|| !targetListener.onResourceReady(result, model, target, dataSource, isFirstResource))) {
574+
boolean anyListenerHandledUpdatingTarget = false;
575+
if (requestListeners != null) {
576+
for (RequestListener<R> listener : requestListeners) {
577+
anyListenerHandledUpdatingTarget |=
578+
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
579+
}
580+
}
581+
anyListenerHandledUpdatingTarget |=
582+
targetListener != null
583+
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
584+
585+
if (!anyListenerHandledUpdatingTarget) {
577586
Transition<? super R> animation =
578587
animationFactory.build(dataSource, isFirstResource);
579588
target.onResourceReady(result, animation);
@@ -609,10 +618,18 @@ private void onLoadFailed(GlideException e, int maxLogLevel) {
609618
isCallingCallbacks = true;
610619
try {
611620
//TODO: what if this is a thumbnail request?
612-
if ((requestListener == null
613-
|| !requestListener.onLoadFailed(e, model, target, isFirstReadyResource()))
614-
&& (targetListener == null
615-
|| !targetListener.onLoadFailed(e, model, target, isFirstReadyResource()))) {
621+
boolean anyListenerHandledUpdatingTarget = false;
622+
if (requestListeners != null) {
623+
for (RequestListener<R> listener : requestListeners) {
624+
anyListenerHandledUpdatingTarget |=
625+
listener.onLoadFailed(e, model, target, isFirstReadyResource());
626+
}
627+
}
628+
anyListenerHandledUpdatingTarget |=
629+
targetListener != null
630+
&& targetListener.onLoadFailed(e, model, target, isFirstReadyResource());
631+
632+
if (!anyListenerHandledUpdatingTarget) {
616633
setErrorPlaceholder();
617634
}
618635
} finally {
@@ -633,14 +650,19 @@ public boolean isEquivalentTo(Request o) {
633650
&& requestOptions.equals(that.requestOptions)
634651
&& priority == that.priority
635652
// We do not want to require that RequestListeners implement equals/hashcode, so we don't
636-
// compare them using equals(). We can however, at least assert that the request listener
637-
// is either present or not present in both requests.
638-
&& (requestListener != null
639-
? that.requestListener != null : that.requestListener == null);
653+
// compare them using equals(). We can however, at least assert that the same amount of
654+
// request listeners are present in both requests
655+
&& listenerCountEquals(this, that);
640656
}
641657
return false;
642658
}
643659

660+
private static boolean listenerCountEquals(SingleRequest<?> first, SingleRequest<?> second) {
661+
int firstListenerCount = first.requestListeners == null ? 0 : first.requestListeners.size();
662+
int secondListenerCount = second.requestListeners == null ? 0 : second.requestListeners.size();
663+
return firstListenerCount == secondListenerCount;
664+
}
665+
644666
private void logV(String message) {
645667
Log.v(TAG, message + " this: " + tag);
646668
}

library/test/src/test/java/com/bumptech/glide/RequestBuilderTest.java

+42-3
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,22 @@
22

33
import static com.bumptech.glide.tests.BackgroundUtil.testInBackground;
44
import static org.mockito.Matchers.any;
5+
import static org.mockito.Matchers.anyBoolean;
56
import static org.mockito.Matchers.eq;
67
import static org.mockito.Matchers.isA;
78
import static org.mockito.Mockito.mock;
9+
import static org.mockito.Mockito.never;
810
import static org.mockito.Mockito.verify;
911
import static org.mockito.Mockito.when;
1012

1113
import android.app.Application;
1214
import android.widget.ImageView;
15+
import com.bumptech.glide.load.DataSource;
16+
import com.bumptech.glide.load.resource.SimpleResource;
1317
import com.bumptech.glide.request.Request;
18+
import com.bumptech.glide.request.RequestListener;
1419
import com.bumptech.glide.request.RequestOptions;
20+
import com.bumptech.glide.request.SingleRequest;
1521
import com.bumptech.glide.request.target.Target;
1622
import com.bumptech.glide.request.target.ViewTarget;
1723
import com.bumptech.glide.tests.BackgroundUtil.BackgroundTester;
@@ -20,6 +26,8 @@
2026
import org.junit.Rule;
2127
import org.junit.Test;
2228
import org.junit.runner.RunWith;
29+
import org.mockito.ArgumentCaptor;
30+
import org.mockito.Captor;
2331
import org.mockito.Mock;
2432
import org.mockito.MockitoAnnotations;
2533
import org.robolectric.RobolectricTestRunner;
@@ -32,8 +40,12 @@
3240
public class RequestBuilderTest {
3341
@Rule public TearDownGlide tearDownGlide = new TearDownGlide();
3442

43+
@Mock private RequestListener<Object> listener1;
44+
@Mock private RequestListener<Object> listener2;
45+
@Mock private Target<Object> target;
3546
@Mock private GlideContext glideContext;
3647
@Mock private RequestManager requestManager;
48+
@Captor private ArgumentCaptor<SingleRequest<Object>> requestCaptor;
3749
private Glide glide;
3850
private Application context;
3951

@@ -57,12 +69,11 @@ public void testThrowsWhenTransitionsOptionsIsNull() {
5769

5870
@Test
5971
public void testDoesNotThrowWithNullModelWhenRequestIsBuilt() {
60-
getNullModelRequest().into(mock(Target.class));
72+
getNullModelRequest().into(target);
6173
}
6274

6375
@Test
6476
public void testAddsNewRequestToRequestTracker() {
65-
Target<Object> target = mock(Target.class);
6677
getNullModelRequest().into(target);
6778

6879
verify(requestManager).track(eq(target), isA(Request.class));
@@ -71,7 +82,6 @@ public void testAddsNewRequestToRequestTracker() {
7182
@Test
7283
public void testRemovesPreviousRequestFromRequestTracker() {
7384
Request previous = mock(Request.class);
74-
Target<Object> target = mock(Target.class);
7585
when(target.getRequest()).thenReturn(previous);
7686

7787
getNullModelRequest().into(target);
@@ -112,6 +122,35 @@ public void runTest() {
112122
});
113123
}
114124

125+
@Test
126+
public void testMultipleRequestListeners() {
127+
getNullModelRequest().addListener(listener1).addListener(listener2).into(target);
128+
verify(requestManager).track(any(Target.class), requestCaptor.capture());
129+
requestCaptor.getValue().onResourceReady(new SimpleResource<>(new Object()), DataSource.LOCAL);
130+
131+
verify(listener1)
132+
.onResourceReady(
133+
any(), any(), isA(Target.class), isA(DataSource.class), anyBoolean());
134+
verify(listener2)
135+
.onResourceReady(
136+
any(), any(), isA(Target.class), isA(DataSource.class), anyBoolean());
137+
}
138+
139+
@Test
140+
public void testListenerApiOverridesListeners() {
141+
getNullModelRequest().addListener(listener1).listener(listener2).into(target);
142+
verify(requestManager).track(any(Target.class), requestCaptor.capture());
143+
requestCaptor.getValue().onResourceReady(new SimpleResource<>(new Object()), DataSource.LOCAL);
144+
145+
// The #listener API removes any previous listeners, so the first listener should not be called.
146+
verify(listener1, never())
147+
.onResourceReady(
148+
any(), any(), isA(Target.class), isA(DataSource.class), anyBoolean());
149+
verify(listener2)
150+
.onResourceReady(
151+
any(), any(), isA(Target.class), isA(DataSource.class), anyBoolean());
152+
}
153+
115154
private RequestBuilder<Object> getNullModelRequest() {
116155
when(glideContext.buildImageViewTarget(isA(ImageView.class), isA(Class.class)))
117156
.thenReturn(mock(ViewTarget.class));

0 commit comments

Comments
 (0)