From cff4f2cc3398daa21e00f76e2c51e4b2bd3952c4 Mon Sep 17 00:00:00 2001 From: Sam Judd Date: Sat, 25 Nov 2017 15:04:42 -0800 Subject: [PATCH] Avoid recycling Bitmaps/Drawables passed to load(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is still best effort in that anyone can register a component that doesn’t follow our conventions and break things, but it should be safer by default at least. --- .../com/bumptech/glide/LoadBitmapTest.java | 141 ++++++++++++++++++ .../com/bumptech/glide/LoadDrawableTest.java | 72 +++++++++ .../glide/NonBitmapDrawableResourcesTest.java | 2 + .../main/java/com/bumptech/glide/Glide.java | 8 +- .../bitmap/BitmapDrawableDecoder.java | 28 ++-- .../bitmap/DrawableTransformation.java | 6 +- .../bitmap/LazyBitmapDrawableResource.java | 50 +++++-- .../resource/bitmap/UnitBitmapDecoder.java | 34 ++++- .../drawable/NonOwnedDrawableResource.java | 2 +- .../transcode/BitmapDrawableTranscoder.java | 21 ++- .../LazyBitmapDrawableResourceTest.java | 90 +++++++++++ .../BitmapDrawableTranscoderTest.java | 5 +- 12 files changed, 414 insertions(+), 45 deletions(-) create mode 100644 instrumentation/src/androidTest/java/com/bumptech/glide/LoadBitmapTest.java create mode 100644 instrumentation/src/androidTest/java/com/bumptech/glide/LoadDrawableTest.java create mode 100644 library/src/test/java/com/bumptech/glide/load/resource/bitmap/LazyBitmapDrawableResourceTest.java diff --git a/instrumentation/src/androidTest/java/com/bumptech/glide/LoadBitmapTest.java b/instrumentation/src/androidTest/java/com/bumptech/glide/LoadBitmapTest.java new file mode 100644 index 0000000000..13928e6185 --- /dev/null +++ b/instrumentation/src/androidTest/java/com/bumptech/glide/LoadBitmapTest.java @@ -0,0 +1,141 @@ +package com.bumptech.glide; + + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.Drawable; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPoolAdapter; +import com.bumptech.glide.load.engine.cache.MemoryCacheAdapter; +import com.bumptech.glide.request.FutureTarget; +import com.bumptech.glide.test.ConcurrencyHelper; +import com.bumptech.glide.test.GlideApp; +import com.bumptech.glide.test.ResourceIds; +import com.bumptech.glide.test.TearDownGlide; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class LoadBitmapTest { + @Rule public final TearDownGlide tearDownGlide = new TearDownGlide(); + private final ConcurrencyHelper concurrency = new ConcurrencyHelper(); + private Context context; + + @Before + public void setUp() { + context = InstrumentationRegistry.getTargetContext(); + } + + @Test + public void clearFromRequestBuilder_asDrawable_withLoadedBitmap_doesNotRecycleBitmap() { + Glide.init(context, new GlideBuilder() + .setMemoryCache(new MemoryCacheAdapter()) + .setBitmapPool(new BitmapPoolAdapter())); + Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), ResourceIds.raw.canonical); + FutureTarget target = + concurrency.wait( + GlideApp.with(context) + .asDrawable() + .load(bitmap) + .submit(100, 100)); + Glide.with(context).clear(target); + + // Allow Glide's resource recycler to run on the main thread. + concurrency.pokeMainThread(); + + assertThat(bitmap.isRecycled()).isFalse(); + } + + @Test + public void transformFromRequestBuilder_asDrawable_withLoadedBitmap_doesNotRecycleBitmap() { + Glide.init(context, new GlideBuilder() + .setMemoryCache(new MemoryCacheAdapter()) + .setBitmapPool(new BitmapPoolAdapter())); + Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), ResourceIds.raw.canonical); + concurrency.wait( + GlideApp.with(context) + .asDrawable() + .load(bitmap) + .centerCrop() + .submit(100, 100)); + + assertThat(bitmap.isRecycled()).isFalse(); + } + + @Test + public void clearFromRequestManager_withLoadedBitmap_doesNotRecycleBitmap() { + Glide.init(context, new GlideBuilder() + .setMemoryCache(new MemoryCacheAdapter()) + .setBitmapPool(new BitmapPoolAdapter())); + Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), ResourceIds.raw.canonical); + FutureTarget target = + concurrency.wait( + GlideApp.with(context) + .load(bitmap) + .submit(100, 100)); + Glide.with(context).clear(target); + + // Allow Glide's resource recycler to run on the main thread. + concurrency.pokeMainThread(); + + assertThat(bitmap.isRecycled()).isFalse(); + } + + @Test + public void transformFromRequestManager_withLoadedBitmap_doesNotRecycleBitmap() { + Glide.init(context, new GlideBuilder() + .setMemoryCache(new MemoryCacheAdapter()) + .setBitmapPool(new BitmapPoolAdapter())); + Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), ResourceIds.raw.canonical); + concurrency.wait( + GlideApp.with(context) + .load(bitmap) + .centerCrop() + .submit(100, 100)); + + assertThat(bitmap.isRecycled()).isFalse(); + } + + @Test + public void clearFromRequestBuilder_withLoadedBitmap_asBitmap_doesNotRecycleBitmap() { + Glide.init(context, new GlideBuilder() + .setMemoryCache(new MemoryCacheAdapter()) + .setBitmapPool(new BitmapPoolAdapter())); + Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), ResourceIds.raw.canonical); + FutureTarget target = + concurrency.wait( + GlideApp.with(context) + .asBitmap() + .load(bitmap) + .submit(100, 100)); + Glide.with(context).clear(target); + + // Allow Glide's resource recycler to run on the main thread. + concurrency.pokeMainThread(); + + assertThat(bitmap.isRecycled()).isFalse(); + } + + @Test + public void transformFromRequestBuilder_withLoadedBitmap_asBitmap_doesNotRecycleBitmap() { + Glide.init(context, new GlideBuilder() + .setMemoryCache(new MemoryCacheAdapter()) + .setBitmapPool(new BitmapPoolAdapter())); + Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), ResourceIds.raw.canonical); + concurrency.wait( + GlideApp.with(context) + .asBitmap() + .load(bitmap) + .centerCrop() + .submit(100, 100)); + + assertThat(bitmap.isRecycled()).isFalse(); + } + +} diff --git a/instrumentation/src/androidTest/java/com/bumptech/glide/LoadDrawableTest.java b/instrumentation/src/androidTest/java/com/bumptech/glide/LoadDrawableTest.java new file mode 100644 index 0000000000..eb5e46e76d --- /dev/null +++ b/instrumentation/src/androidTest/java/com/bumptech/glide/LoadDrawableTest.java @@ -0,0 +1,72 @@ +package com.bumptech.glide; + + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPoolAdapter; +import com.bumptech.glide.load.engine.cache.MemoryCacheAdapter; +import com.bumptech.glide.request.FutureTarget; +import com.bumptech.glide.test.ConcurrencyHelper; +import com.bumptech.glide.test.GlideApp; +import com.bumptech.glide.test.ResourceIds; +import com.bumptech.glide.test.TearDownGlide; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class LoadDrawableTest { + @Rule public final TearDownGlide tearDownGlide = new TearDownGlide(); + private final ConcurrencyHelper concurrency = new ConcurrencyHelper(); + + private Context context; + + @Before + public void setUp() { + context = InstrumentationRegistry.getTargetContext(); + } + + @Test + public void clear_withLoadedBitmapDrawable_doesNotRecycleBitmap() { + Glide.init(context, new GlideBuilder() + .setMemoryCache(new MemoryCacheAdapter()) + .setBitmapPool(new BitmapPoolAdapter())); + Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), ResourceIds.raw.canonical); + BitmapDrawable drawable = new BitmapDrawable(context.getResources(), bitmap); + FutureTarget target = + concurrency.wait( + GlideApp.with(context) + .load(drawable) + .submit(100, 100)); + Glide.with(context).clear(target); + + // Allow Glide's resource recycler to run on the main thread. + concurrency.pokeMainThread(); + + assertThat(bitmap.isRecycled()).isFalse(); + } + + @Test + public void transform_withLoadedBitmapDrawable_doesNotRecycleBitmap() { + Glide.init(context, new GlideBuilder() + .setMemoryCache(new MemoryCacheAdapter()) + .setBitmapPool(new BitmapPoolAdapter())); + Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), ResourceIds.raw.canonical); + BitmapDrawable drawable = new BitmapDrawable(context.getResources(), bitmap); + concurrency.wait( + GlideApp.with(context) + .load(drawable) + .centerCrop() + .submit(100, 100)); + + assertThat(bitmap.isRecycled()).isFalse(); + } +} diff --git a/instrumentation/src/androidTest/java/com/bumptech/glide/NonBitmapDrawableResourcesTest.java b/instrumentation/src/androidTest/java/com/bumptech/glide/NonBitmapDrawableResourcesTest.java index 3fa1cba76f..e41db57054 100644 --- a/instrumentation/src/androidTest/java/com/bumptech/glide/NonBitmapDrawableResourcesTest.java +++ b/instrumentation/src/androidTest/java/com/bumptech/glide/NonBitmapDrawableResourcesTest.java @@ -30,6 +30,7 @@ import org.junit.rules.ExpectedException; import org.junit.rules.TestName; import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class NonBitmapDrawableResourcesTest { @@ -41,6 +42,7 @@ public class NonBitmapDrawableResourcesTest { @Before public void setUp() { + MockitoAnnotations.initMocks(this); context = InstrumentationRegistry.getTargetContext(); } diff --git a/library/src/main/java/com/bumptech/glide/Glide.java b/library/src/main/java/com/bumptech/glide/Glide.java index 082b9a6032..ea902a5650 100644 --- a/library/src/main/java/com/bumptech/glide/Glide.java +++ b/library/src/main/java/com/bumptech/glide/Glide.java @@ -355,17 +355,17 @@ Registry.BUCKET_BITMAP, Bitmap.class, Bitmap.class, new UnitBitmapDecoder()) Registry.BUCKET_BITMAP_DRAWABLE, ByteBuffer.class, BitmapDrawable.class, - new BitmapDrawableDecoder<>(resources, bitmapPool, byteBufferBitmapDecoder)) + new BitmapDrawableDecoder<>(resources, byteBufferBitmapDecoder)) .append( Registry.BUCKET_BITMAP_DRAWABLE, InputStream.class, BitmapDrawable.class, - new BitmapDrawableDecoder<>(resources, bitmapPool, streamBitmapDecoder)) + new BitmapDrawableDecoder<>(resources, streamBitmapDecoder)) .append( Registry.BUCKET_BITMAP_DRAWABLE, ParcelFileDescriptor.class, BitmapDrawable.class, - new BitmapDrawableDecoder<>(resources, bitmapPool, videoBitmapDecoder)) + new BitmapDrawableDecoder<>(resources, videoBitmapDecoder)) .append(BitmapDrawable.class, new BitmapDrawableEncoder(bitmapPool, bitmapEncoder)) /* GIFs */ .append( @@ -440,7 +440,7 @@ Uri.class, Bitmap.class, new ResourceBitmapDecoder(resourceDrawableDecoder, bitm .register( Bitmap.class, BitmapDrawable.class, - new BitmapDrawableTranscoder(resources, bitmapPool)) + new BitmapDrawableTranscoder(resources)) .register(Bitmap.class, byte[].class, new BitmapBytesTranscoder()) .register(GifDrawable.class, byte[].class, new GifDrawableBytesTranscoder()); diff --git a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/BitmapDrawableDecoder.java b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/BitmapDrawableDecoder.java index bcc996f349..e9b954c4ad 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/BitmapDrawableDecoder.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/BitmapDrawableDecoder.java @@ -4,7 +4,7 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; -import com.bumptech.glide.Glide; +import android.support.annotation.NonNull; import com.bumptech.glide.load.Options; import com.bumptech.glide.load.ResourceDecoder; import com.bumptech.glide.load.engine.Resource; @@ -21,18 +21,28 @@ public class BitmapDrawableDecoder implements ResourceDecoder decoder; private final Resources resources; - private final BitmapPool bitmapPool; // Public API. - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "WeakerAccess"}) public BitmapDrawableDecoder(Context context, ResourceDecoder decoder) { - this(context.getResources(), Glide.get(context).getBitmapPool(), decoder); + this(context.getResources(), decoder); } - public BitmapDrawableDecoder(Resources resources, BitmapPool bitmapPool, + /** + * @deprecated Use {@link #BitmapDrawableDecoder(Context, ResourceDecoder)}, {@code bitmapPool} + * is ignored. + */ + @Deprecated + public BitmapDrawableDecoder( + Resources resources, + @SuppressWarnings("unused") BitmapPool bitmapPool, ResourceDecoder decoder) { + this(resources, decoder); + } + + public BitmapDrawableDecoder( + @NonNull Resources resources, @NonNull ResourceDecoder decoder) { this.resources = Preconditions.checkNotNull(resources); - this.bitmapPool = Preconditions.checkNotNull(bitmapPool); this.decoder = Preconditions.checkNotNull(decoder); } @@ -45,10 +55,6 @@ public boolean handles(DataType source, Options options) throws IOException { public Resource decode(DataType source, int width, int height, Options options) throws IOException { Resource bitmapResource = decoder.decode(source, width, height, options); - if (bitmapResource == null) { - return null; - } - - return LazyBitmapDrawableResource.obtain(resources, bitmapPool, bitmapResource.get()); + return LazyBitmapDrawableResource.obtain(resources, bitmapResource); } } diff --git a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/DrawableTransformation.java b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/DrawableTransformation.java index 54b7abc515..7fef9697ee 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/DrawableTransformation.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/DrawableTransformation.java @@ -63,15 +63,15 @@ public Resource transform(Context context, Resource resource transformedBitmapResource.recycle(); return resource; } else { - return newDrawableResource(context, transformedBitmapResource.get()); + return newDrawableResource(context, transformedBitmapResource); } } @SuppressWarnings("unchecked") private Resource newDrawableResource( - Context context, Bitmap transformed) { + Context context, Resource transformed) { Resource result = - LazyBitmapDrawableResource.obtain(context, transformed); + LazyBitmapDrawableResource.obtain(context.getResources(), transformed); return (Resource) result; } diff --git a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/LazyBitmapDrawableResource.java b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/LazyBitmapDrawableResource.java index 0228f05dbc..42058fca85 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/LazyBitmapDrawableResource.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/LazyBitmapDrawableResource.java @@ -4,12 +4,13 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.Initializable; import com.bumptech.glide.load.engine.Resource; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; import com.bumptech.glide.util.Preconditions; -import com.bumptech.glide.util.Util; /** * Lazily allocates a {@link android.graphics.drawable.BitmapDrawable} from a given @@ -18,23 +19,46 @@ public final class LazyBitmapDrawableResource implements Resource, Initializable { - private final Bitmap bitmap; private final Resources resources; - private final BitmapPool bitmapPool; + private final Resource bitmapResource; + /** + * @deprecated Use {@link #obtain(Resources, Resource)} instead, it can be unsafe to extract + * {@link Bitmap}s from their wrapped {@link Resource}. + */ + @Deprecated public static LazyBitmapDrawableResource obtain(Context context, Bitmap bitmap) { - return obtain(context.getResources(), Glide.get(context).getBitmapPool(), bitmap); + return + (LazyBitmapDrawableResource) + obtain( + context.getResources(), + BitmapResource.obtain(bitmap, Glide.get(context).getBitmapPool())); } + /** + * @deprecated Use {@link #obtain(Resources, Resource)} instead, it can be unsafe to extract + * {@link Bitmap}s from their wrapped {@link Resource}. + */ + @Deprecated public static LazyBitmapDrawableResource obtain(Resources resources, BitmapPool bitmapPool, Bitmap bitmap) { - return new LazyBitmapDrawableResource(resources, bitmapPool, bitmap); + return + (LazyBitmapDrawableResource) obtain(resources, BitmapResource.obtain(bitmap, bitmapPool)); } - private LazyBitmapDrawableResource(Resources resources, BitmapPool bitmapPool, Bitmap bitmap) { + @Nullable + public static Resource obtain( + @NonNull Resources resources, @Nullable Resource bitmapResource) { + if (bitmapResource == null) { + return null; + } + return new LazyBitmapDrawableResource(resources, bitmapResource); + + } + + private LazyBitmapDrawableResource(Resources resources, Resource bitmapResource) { this.resources = Preconditions.checkNotNull(resources); - this.bitmapPool = Preconditions.checkNotNull(bitmapPool); - this.bitmap = Preconditions.checkNotNull(bitmap); + this.bitmapResource = Preconditions.checkNotNull(bitmapResource); } @Override @@ -44,21 +68,23 @@ public Class getResourceClass() { @Override public BitmapDrawable get() { - return new BitmapDrawable(resources, bitmap); + return new BitmapDrawable(resources, bitmapResource.get()); } @Override public int getSize() { - return Util.getBitmapByteSize(bitmap); + return bitmapResource.getSize(); } @Override public void recycle() { - bitmapPool.put(bitmap); + bitmapResource.recycle(); } @Override public void initialize() { - bitmap.prepareToDraw(); + if (bitmapResource instanceof Initializable) { + ((Initializable) bitmapResource).initialize(); + } } } diff --git a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/UnitBitmapDecoder.java b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/UnitBitmapDecoder.java index 57e28337e2..2b91d20812 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/UnitBitmapDecoder.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/UnitBitmapDecoder.java @@ -4,7 +4,7 @@ import com.bumptech.glide.load.Options; import com.bumptech.glide.load.ResourceDecoder; import com.bumptech.glide.load.engine.Resource; -import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPoolAdapter; +import com.bumptech.glide.util.Util; import java.io.IOException; /** @@ -12,7 +12,6 @@ * so that the given {@link Bitmap} is not recycled. */ public final class UnitBitmapDecoder implements ResourceDecoder { - private static final BitmapPoolAdapter BITMAP_POOL = new BitmapPoolAdapter(); @Override public boolean handles(Bitmap source, Options options) throws IOException { @@ -22,6 +21,35 @@ public boolean handles(Bitmap source, Options options) throws IOException { @Override public Resource decode(Bitmap source, int width, int height, Options options) throws IOException { - return new BitmapResource(source, BITMAP_POOL); + return new NonOwnedBitmapResource(source); + } + + private static final class NonOwnedBitmapResource implements Resource { + + private final Bitmap bitmap; + + NonOwnedBitmapResource(Bitmap bitmap) { + this.bitmap = bitmap; + } + + @Override + public Class getResourceClass() { + return Bitmap.class; + } + + @Override + public Bitmap get() { + return bitmap; + } + + @Override + public int getSize() { + return Util.getBitmapByteSize(bitmap); + } + + @Override + public void recycle() { + // Do nothing. + } } } diff --git a/library/src/main/java/com/bumptech/glide/load/resource/drawable/NonOwnedDrawableResource.java b/library/src/main/java/com/bumptech/glide/load/resource/drawable/NonOwnedDrawableResource.java index 789d5d9257..616976a6c6 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/drawable/NonOwnedDrawableResource.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/drawable/NonOwnedDrawableResource.java @@ -10,7 +10,7 @@ final class NonOwnedDrawableResource extends DrawableResource { @SuppressWarnings("unchecked") - public static Resource newInstance(Drawable drawable) { + static Resource newInstance(Drawable drawable) { return new NonOwnedDrawableResource(drawable); } diff --git a/library/src/main/java/com/bumptech/glide/load/resource/transcode/BitmapDrawableTranscoder.java b/library/src/main/java/com/bumptech/glide/load/resource/transcode/BitmapDrawableTranscoder.java index 12b6b79d32..a5a61ead99 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/transcode/BitmapDrawableTranscoder.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/transcode/BitmapDrawableTranscoder.java @@ -4,7 +4,7 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; -import com.bumptech.glide.Glide; +import android.support.annotation.NonNull; import com.bumptech.glide.load.Options; import com.bumptech.glide.load.engine.Resource; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; @@ -17,21 +17,28 @@ */ public class BitmapDrawableTranscoder implements ResourceTranscoder { private final Resources resources; - private final BitmapPool bitmapPool; // Public API. @SuppressWarnings("unused") - public BitmapDrawableTranscoder(Context context) { - this(context.getResources(), Glide.get(context).getBitmapPool()); + public BitmapDrawableTranscoder(@NonNull Context context) { + this(context.getResources()); } - public BitmapDrawableTranscoder(Resources resources, BitmapPool bitmapPool) { + /** + * @deprecated Use {@link #BitmapDrawableTranscoder(Resources)}, {@code bitmapPool} is unused. + */ + @Deprecated + public BitmapDrawableTranscoder( + @NonNull Resources resources, @SuppressWarnings("unused") BitmapPool bitmapPool) { + this(resources); + } + + public BitmapDrawableTranscoder(@NonNull Resources resources) { this.resources = Preconditions.checkNotNull(resources); - this.bitmapPool = Preconditions.checkNotNull(bitmapPool); } @Override public Resource transcode(Resource toTranscode, Options options) { - return LazyBitmapDrawableResource.obtain(resources, bitmapPool, toTranscode.get()); + return LazyBitmapDrawableResource.obtain(resources, toTranscode); } } diff --git a/library/src/test/java/com/bumptech/glide/load/resource/bitmap/LazyBitmapDrawableResourceTest.java b/library/src/test/java/com/bumptech/glide/load/resource/bitmap/LazyBitmapDrawableResourceTest.java new file mode 100644 index 0000000000..1ad62cac21 --- /dev/null +++ b/library/src/test/java/com/bumptech/glide/load/resource/bitmap/LazyBitmapDrawableResourceTest.java @@ -0,0 +1,90 @@ +package com.bumptech.glide.load.resource.bitmap; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import com.bumptech.glide.load.engine.Initializable; +import com.bumptech.glide.load.engine.Resource; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(RobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class LazyBitmapDrawableResourceTest { + @Mock private Resource bitmapResource; + private LazyBitmapDrawableResource resource; + private Resources resources; + private Bitmap bitmap; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); + when(bitmapResource.get()).thenReturn(bitmap); + + resources = RuntimeEnvironment.application.getResources(); + resource = + (LazyBitmapDrawableResource) LazyBitmapDrawableResource.obtain(resources, bitmapResource); + } + + @Test + public void obtain_withNullBitmapResource_returnsNull() { + assertThat(LazyBitmapDrawableResource.obtain(resources, null)).isNull(); + } + + @Test + public void getSize_returnsSizeOfWrappedResource() { + when(bitmapResource.getSize()).thenReturn(100); + assertThat(resource.getSize()).isEqualTo(100); + } + + @Test + public void recycle_callsRecycleOnWrappedResource() { + resource.recycle(); + verify(bitmapResource).recycle(); + } + + @Test + public void recycle_doesNotRecycleWrappedBitmap() { + resource.recycle(); + assertThat(bitmap.isRecycled()).isFalse(); + } + + @Test + public void get_returnsDrawableContainingWrappedBitmap() { + BitmapDrawable drawable = resource.get(); + assertThat(drawable.getBitmap()).isSameAs(bitmap); + } + + @Test + public void initialize_withNonInitializableResource_doesNothing() { + resource.initialize(); + } + + @Test + public void initialize_withWrappedInitializableResource_callsInitializeOnWrapped() { + InitializableBitmapResource bitmapResource = mock(InitializableBitmapResource.class); + resource = + (LazyBitmapDrawableResource) LazyBitmapDrawableResource.obtain(resources, bitmapResource); + resource.initialize(); + + verify(bitmapResource).initialize(); + } + + private interface InitializableBitmapResource extends Initializable, + Resource { + // Intentionally empty. + } +} diff --git a/library/src/test/java/com/bumptech/glide/load/resource/transcode/BitmapDrawableTranscoderTest.java b/library/src/test/java/com/bumptech/glide/load/resource/transcode/BitmapDrawableTranscoderTest.java index 0707a342aa..b3c195657a 100644 --- a/library/src/test/java/com/bumptech/glide/load/resource/transcode/BitmapDrawableTranscoderTest.java +++ b/library/src/test/java/com/bumptech/glide/load/resource/transcode/BitmapDrawableTranscoderTest.java @@ -2,14 +2,12 @@ import static com.bumptech.glide.tests.Util.mockResource; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import com.bumptech.glide.load.Options; import com.bumptech.glide.load.engine.Resource; -import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -24,8 +22,7 @@ public class BitmapDrawableTranscoderTest { @Before public void setUp() { - transcoder = new BitmapDrawableTranscoder(RuntimeEnvironment.application.getResources(), - mock(BitmapPool.class)); + transcoder = new BitmapDrawableTranscoder(RuntimeEnvironment.application.getResources()); } @Test