From 37127f0f817d4a11dfdcc447946397b5288de593 Mon Sep 17 00:00:00 2001 From: judds Date: Thu, 23 Aug 2018 09:22:17 -0700 Subject: [PATCH] Add global/activity/fragment scoped RequestListener API to Glide. After this change, callers will be able to add one or more RequestListeners either to all requests started with Glide, or to all requests started within a particular Activity or Fragment. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=209947609 --- .../main/java/com/bumptech/glide/Glide.java | 19 +++++--- .../java/com/bumptech/glide/GlideBuilder.java | 43 ++++++++++++++++++- .../java/com/bumptech/glide/GlideContext.java | 9 ++++ .../com/bumptech/glide/RequestBuilder.java | 21 ++++++++- .../com/bumptech/glide/RequestManager.java | 38 ++++++++++++++++ .../com/bumptech/glide/GlideContextTest.java | 2 + 6 files changed, 124 insertions(+), 8 deletions(-) diff --git a/library/src/main/java/com/bumptech/glide/Glide.java b/library/src/main/java/com/bumptech/glide/Glide.java index f200fa96cc..89b950cd8f 100644 --- a/library/src/main/java/com/bumptech/glide/Glide.java +++ b/library/src/main/java/com/bumptech/glide/Glide.java @@ -22,6 +22,7 @@ import android.view.View; import com.bumptech.glide.gifdecoder.GifDecoder; import com.bumptech.glide.load.DecodeFormat; +import com.bumptech.glide.load.ImageHeaderParser; import com.bumptech.glide.load.ResourceDecoder; import com.bumptech.glide.load.data.InputStreamRewinder; import com.bumptech.glide.load.engine.Engine; @@ -76,6 +77,7 @@ import com.bumptech.glide.manager.ConnectivityMonitorFactory; import com.bumptech.glide.manager.RequestManagerRetriever; import com.bumptech.glide.module.ManifestParser; +import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.ImageViewTargetFactory; import com.bumptech.glide.request.target.Target; @@ -319,7 +321,8 @@ private static void throwIncorrectGlideModule(Exception e) { @NonNull ConnectivityMonitorFactory connectivityMonitorFactory, int logLevel, @NonNull RequestOptions defaultRequestOptions, - @NonNull Map, TransitionOptions> defaultTransitionOptions) { + @NonNull Map, TransitionOptions> defaultTransitionOptions, + @NonNull List> defaultRequestListeners) { this.engine = engine; this.bitmapPool = bitmapPool; this.arrayPool = arrayPool; @@ -342,10 +345,15 @@ private static void throwIncorrectGlideModule(Exception e) { } registry.register(new DefaultImageHeaderParser()); - Downsampler downsampler = new Downsampler(registry.getImageHeaderParsers(), - resources.getDisplayMetrics(), bitmapPool, arrayPool); + List imageHeaderParsers = registry.getImageHeaderParsers(); + Downsampler downsampler = + new Downsampler( + imageHeaderParsers, + resources.getDisplayMetrics(), + bitmapPool, + arrayPool); ByteBufferGifDecoder byteBufferGifDecoder = - new ByteBufferGifDecoder(context, registry.getImageHeaderParsers(), bitmapPool, arrayPool); + new ByteBufferGifDecoder(context, imageHeaderParsers, bitmapPool, arrayPool); ResourceDecoder parcelFileDescriptorVideoDecoder = VideoDecoder.parcel(bitmapPool); ByteBufferBitmapDecoder byteBufferBitmapDecoder = new ByteBufferBitmapDecoder(downsampler); @@ -409,7 +417,7 @@ Registry.BUCKET_BITMAP, Bitmap.class, Bitmap.class, new UnitBitmapDecoder()) Registry.BUCKET_GIF, InputStream.class, GifDrawable.class, - new StreamGifDecoder(registry.getImageHeaderParsers(), byteBufferGifDecoder, arrayPool)) + new StreamGifDecoder(imageHeaderParsers, byteBufferGifDecoder, arrayPool)) .append(Registry.BUCKET_GIF, ByteBuffer.class, GifDrawable.class, byteBufferGifDecoder) .append(GifDrawable.class, new GifDrawableEncoder()) /* GIF Frames */ @@ -512,6 +520,7 @@ Uri.class, Bitmap.class, new ResourceBitmapDecoder(resourceDrawableDecoder, bitm imageViewTargetFactory, defaultRequestOptions, defaultTransitionOptions, + defaultRequestListeners, engine, logLevel); } diff --git a/library/src/main/java/com/bumptech/glide/GlideBuilder.java b/library/src/main/java/com/bumptech/glide/GlideBuilder.java index 3112582078..bdfae965b3 100644 --- a/library/src/main/java/com/bumptech/glide/GlideBuilder.java +++ b/library/src/main/java/com/bumptech/glide/GlideBuilder.java @@ -6,7 +6,9 @@ import android.support.annotation.Nullable; import android.support.v4.util.ArrayMap; import android.util.Log; +import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.engine.Engine; +import com.bumptech.glide.load.engine.GlideException; import com.bumptech.glide.load.engine.bitmap_recycle.ArrayPool; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPoolAdapter; @@ -22,8 +24,12 @@ import com.bumptech.glide.manager.DefaultConnectivityMonitorFactory; import com.bumptech.glide.manager.RequestManagerRetriever; import com.bumptech.glide.manager.RequestManagerRetriever.RequestManagerFactory; +import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.Target; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Map; /** @@ -46,6 +52,8 @@ public final class GlideBuilder { private RequestManagerFactory requestManagerFactory; private GlideExecutor animationExecutor; private boolean isActiveResourceRetentionAllowed; + @Nullable + private List> defaultRequestListeners; /** * Sets the {@link com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool} implementation to use @@ -372,6 +380,32 @@ public GlideBuilder setIsActiveResourceRetentionAllowed( return this; } + /** + * Adds a global {@link RequestListener} that will be added to every request started with Glide. + * + *

Multiple {@link RequestListener}s can be added here, in {@link RequestManager} scopes or + * to individual {@link RequestBuilder}s. {@link RequestListener}s are called in the order they're + * added. Even if an earlier {@link RequestListener} returns {@code true} from + * {@link RequestListener#onLoadFailed(GlideException, Object, Target, boolean)} or + * {@link RequestListener#onResourceReady(Object, Object, Target, DataSource, boolean)}, it will + * not prevent subsequent {@link RequestListener}s from being called. + * + *

Because Glide requests can be started for any number of individual resource types, any + * listener added here has to accept any generic resource type in + * {@link RequestListener#onResourceReady(Object, Object, Target, DataSource, boolean)}. If you + * must base the behavior of the listener on the resource type, you will need to use + * {@code instanceof} to do so. It's not safe to cast resource types without first checking + * with {@code instanceof}. + */ + @NonNull + public GlideBuilder addGlobalRequestListener(@NonNull RequestListener listener) { + if (defaultRequestListeners == null) { + defaultRequestListeners = new ArrayList<>(); + } + defaultRequestListeners.add(listener); + return this; + } + void setRequestManagerFactory(@Nullable RequestManagerFactory factory) { this.requestManagerFactory = factory; } @@ -437,6 +471,12 @@ Glide build(@NonNull Context context) { isActiveResourceRetentionAllowed); } + if (defaultRequestListeners == null) { + defaultRequestListeners = Collections.emptyList(); + } else { + defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners); + } + RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(requestManagerFactory); @@ -450,6 +490,7 @@ Glide build(@NonNull Context context) { connectivityMonitorFactory, logLevel, defaultRequestOptions.lock(), - defaultTransitionOptions); + defaultTransitionOptions, + defaultRequestListeners); } } diff --git a/library/src/main/java/com/bumptech/glide/GlideContext.java b/library/src/main/java/com/bumptech/glide/GlideContext.java index eeb98cc5f7..a587b19bd4 100644 --- a/library/src/main/java/com/bumptech/glide/GlideContext.java +++ b/library/src/main/java/com/bumptech/glide/GlideContext.java @@ -9,9 +9,11 @@ import android.widget.ImageView; import com.bumptech.glide.load.engine.Engine; import com.bumptech.glide.load.engine.bitmap_recycle.ArrayPool; +import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.ImageViewTargetFactory; import com.bumptech.glide.request.target.ViewTarget; +import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -28,6 +30,7 @@ public class GlideContext extends ContextWrapper { private final Registry registry; private final ImageViewTargetFactory imageViewTargetFactory; private final RequestOptions defaultRequestOptions; + private final List> defaultRequestListeners; private final Map, TransitionOptions> defaultTransitionOptions; private final Engine engine; private final int logLevel; @@ -39,6 +42,7 @@ public GlideContext( @NonNull ImageViewTargetFactory imageViewTargetFactory, @NonNull RequestOptions defaultRequestOptions, @NonNull Map, TransitionOptions> defaultTransitionOptions, + @NonNull List> defaultRequestListeners, @NonNull Engine engine, int logLevel) { super(context.getApplicationContext()); @@ -46,6 +50,7 @@ public GlideContext( this.registry = registry; this.imageViewTargetFactory = imageViewTargetFactory; this.defaultRequestOptions = defaultRequestOptions; + this.defaultRequestListeners = defaultRequestListeners; this.defaultTransitionOptions = defaultTransitionOptions; this.engine = engine; this.logLevel = logLevel; @@ -53,6 +58,10 @@ public GlideContext( mainHandler = new Handler(Looper.getMainLooper()); } + public List> getDefaultRequestListeners() { + return defaultRequestListeners; + } + public RequestOptions getDefaultRequestOptions() { return defaultRequestOptions; } diff --git a/library/src/main/java/com/bumptech/glide/RequestBuilder.java b/library/src/main/java/com/bumptech/glide/RequestBuilder.java index 1438311e2d..54c520e0aa 100644 --- a/library/src/main/java/com/bumptech/glide/RequestBuilder.java +++ b/library/src/main/java/com/bumptech/glide/RequestBuilder.java @@ -4,6 +4,7 @@ import static com.bumptech.glide.request.RequestOptions.signatureOf; import static com.bumptech.glide.request.RequestOptions.skipMemoryCacheOf; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; @@ -76,8 +77,12 @@ public class RequestBuilder implements Cloneable, private boolean isModelSet; private boolean isThumbnailBuilt; - protected RequestBuilder(Glide glide, RequestManager requestManager, - Class transcodeClass, Context context) { + @SuppressLint("CheckResult") + protected RequestBuilder( + @NonNull Glide glide, + RequestManager requestManager, + Class transcodeClass, + Context context) { this.glide = glide; this.requestManager = requestManager; this.transcodeClass = transcodeClass; @@ -86,6 +91,8 @@ protected RequestBuilder(Glide glide, RequestManager requestManager, this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass); this.requestOptions = defaultRequestOptions; this.glideContext = glide.getGlideContext(); + + initRequestListeners(requestManager.getDefaultRequestListeners()); } protected RequestBuilder(Class transcodeClass, RequestBuilder other) { @@ -95,6 +102,16 @@ protected RequestBuilder(Class transcodeClass, RequestBuilder requestOptions = other.requestOptions; } + // Casting from Object to a specific type is always safe. + @SuppressWarnings("unchecked") + // addListener always returns the same instance. + @SuppressLint("CheckResult") + private void initRequestListeners(List> requestListeners) { + for (RequestListener listener : requestListeners) { + addListener((RequestListener) listener); + } + } + /** * Applies the given options to the request. * diff --git a/library/src/main/java/com/bumptech/glide/RequestManager.java b/library/src/main/java/com/bumptech/glide/RequestManager.java index 139df6f433..9e0cdb6c33 100644 --- a/library/src/main/java/com/bumptech/glide/RequestManager.java +++ b/library/src/main/java/com/bumptech/glide/RequestManager.java @@ -16,7 +16,9 @@ import android.support.annotation.Nullable; import android.support.annotation.RawRes; import android.view.View; +import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.engine.GlideException; import com.bumptech.glide.load.resource.gif.GifDrawable; import com.bumptech.glide.manager.ConnectivityMonitor; import com.bumptech.glide.manager.ConnectivityMonitorFactory; @@ -26,6 +28,7 @@ import com.bumptech.glide.manager.RequestTracker; import com.bumptech.glide.manager.TargetTracker; import com.bumptech.glide.request.Request; +import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.Target; import com.bumptech.glide.request.target.ViewTarget; @@ -34,6 +37,8 @@ import com.bumptech.glide.util.Util; import java.io.File; import java.net.URL; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; /** * A class for managing and starting requests for Glide. Can use activity, fragment and connectivity @@ -69,6 +74,10 @@ public void run() { }; private final Handler mainHandler = new Handler(Looper.getMainLooper()); private final ConnectivityMonitor connectivityMonitor; + // Adding default listeners should be much less common than starting new requests. We want + // some way of making sure that requests don't mutate our listeners without creating a new copy of + // the list each time a request is started. + private final CopyOnWriteArrayList> defaultRequestListeners; private RequestOptions requestOptions; @@ -115,6 +124,8 @@ public RequestManager( } lifecycle.addListener(connectivityMonitor); + defaultRequestListeners = + new CopyOnWriteArrayList<>(glide.getGlideContext().getDefaultRequestListeners()); setRequestOptions(glide.getGlideContext().getDefaultRequestOptions()); glide.registerRequestManager(this); @@ -174,6 +185,29 @@ public RequestManager setDefaultRequestOptions(@NonNull RequestOptions requestOp return this; } + /** + * Adds a default {@link RequestListener} that will be added to every request started with this + * {@link RequestManager}. + * + *

Multiple {@link RequestListener}s can be added here, in {@link RequestManager} scopes or + * to individual {@link RequestBuilder}s. {@link RequestListener}s are called in the order they're + * added. Even if an earlier {@link RequestListener} returns {@code true} from + * {@link RequestListener#onLoadFailed(GlideException, Object, Target, boolean)} or + * {@link RequestListener#onResourceReady(Object, Object, Target, DataSource, boolean)}, it will + * not prevent subsequent {@link RequestListener}s from being called. + * + *

Because Glide requests can be started for any number of individual resource types, any + * listener added here has to accept any generic resource type in + * {@link RequestListener#onResourceReady(Object, Object, Target, DataSource, boolean)}. If you + * must base the behavior of the listener on the resource type, you will need to use + * {@code instanceof} to do so. It's not safe to cast resource types without first checking + * with {@code instanceof}. + */ + public RequestManager addDefaultRequestListener(RequestListener requestListener) { + defaultRequestListeners.add(requestListener); + return this; + } + /** * Returns true if loads for this {@link RequestManager} are currently paused. * @@ -614,6 +648,10 @@ void track(@NonNull Target target, @NonNull Request request) { requestTracker.runRequest(request); } + List> getDefaultRequestListeners() { + return defaultRequestListeners; + } + RequestOptions getDefaultRequestOptions() { return requestOptions; } diff --git a/library/test/src/test/java/com/bumptech/glide/GlideContextTest.java b/library/test/src/test/java/com/bumptech/glide/GlideContextTest.java index 2281d57069..dc5df1cbd3 100644 --- a/library/test/src/test/java/com/bumptech/glide/GlideContextTest.java +++ b/library/test/src/test/java/com/bumptech/glide/GlideContextTest.java @@ -14,6 +14,7 @@ import com.bumptech.glide.load.resource.gif.GifDrawable; import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.ImageViewTargetFactory; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.junit.Before; @@ -41,6 +42,7 @@ public void setUp() { new ImageViewTargetFactory(), new RequestOptions(), transitionOptions, + /*defaultRequestListeners=*/ Collections.emptyList(), mock(Engine.class), Log.DEBUG); }