Skip to content

Commit bbb25af

Browse files
committed
Pass a non-application Context into Glide’s Requests.
We can use the default themes from Activities and/or Context wrappers to obtain placeholder Drawables, which can be more efficient than forcing callers to call getDrawable when building their request and more powerful/neater than having callers pass in a Theme. This change does NOT pass through the Context or Themes to the decoder that loads non-Bitmap drawables on a background thread to avoid memory leaks. Fixes #1267.
1 parent 702386e commit bbb25af

File tree

12 files changed

+89
-32
lines changed

12 files changed

+89
-32
lines changed

annotation/compiler/src/main/java/com/bumptech/glide/annotation/compiler/RequestBuilderGenerator.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -359,14 +359,17 @@ private List<MethodSpec> generateConstructors() {
359359
.addStatement("super($N, $N)", "transcodeClass", "other")
360360
.build();
361361

362+
ClassName context = ClassName.get("android.content", "Context");
362363
ClassName glide = ClassName.get("com.bumptech.glide", "Glide");
363364
ClassName requestManager = ClassName.get("com.bumptech.glide", "RequestManager");
364365
MethodSpec secondConstructor =
365366
MethodSpec.constructorBuilder()
366367
.addParameter(glide, "glide")
367368
.addParameter(requestManager, "requestManager")
368369
.addParameter(classOfTranscodeType, "transcodeClass")
369-
.addStatement("super($N, $N ,$N)", "glide", "requestManager", "transcodeClass")
370+
.addParameter(context, "context")
371+
.addStatement(
372+
"super($N, $N ,$N, $N)", "glide", "requestManager", "transcodeClass", "context")
370373
.build();
371374
return ImmutableList.of(firstConstructor, secondConstructor);
372375
}

annotation/compiler/src/main/java/com/bumptech/glide/annotation/compiler/RequestManagerFactoryGenerator.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ final class RequestManagerFactoryGenerator {
3939
"com.bumptech.glide.manager.RequestManagerRetriever.RequestManagerFactory";
4040
private static final String REQUEST_MANAGER_QUALIFIED_NAME =
4141
"com.bumptech.glide.RequestManager";
42+
private static final ClassName CONTEXT_CLASS_NAME =
43+
ClassName.get("android.content", "Context");
4244

4345
static final String GENERATED_REQUEST_MANAGER_FACTORY_PACKAGE_NAME =
4446
"com.bumptech.glide";
@@ -79,8 +81,9 @@ TypeSpec generate(String generatedCodePackageName, TypeSpec generatedRequestMana
7981
.addParameter(ClassName.get(glideType), "glide")
8082
.addParameter(ClassName.get(lifecycleType), "lifecycle")
8183
.addParameter(ClassName.get(requestManagerTreeNodeType), "treeNode")
84+
.addParameter(CONTEXT_CLASS_NAME, "context")
8285
.addStatement(
83-
"return new $T(glide, lifecycle, treeNode)",
86+
"return new $T(glide, lifecycle, treeNode, context)",
8487
ClassName.get(generatedCodePackageName, generatedRequestManagerSpec.name))
8588
.build()
8689
)

annotation/compiler/src/main/java/com/bumptech/glide/annotation/compiler/RequestManagerGenerator.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ final class RequestManagerGenerator {
5959
"com.bumptech.glide.manager.RequestManagerTreeNode";
6060
private static final ClassName CHECK_RESULT_CLASS_NAME =
6161
ClassName.get("android.support.annotation", "CheckResult");
62+
private static final ClassName CONTEXT_CLASS_NAME =
63+
ClassName.get("android.content", "Context");
6264

6365
private static final String GENERATED_REQUEST_MANAGER_SIMPLE_NAME =
6466
"GlideRequests";
@@ -128,7 +130,8 @@ private MethodSpec generateCallSuperConstructor() {
128130
.addParameter(ClassName.get(glideType), "glide")
129131
.addParameter(ClassName.get(lifecycleType), "lifecycle")
130132
.addParameter(ClassName.get(requestManagerTreeNodeType), "treeNode")
131-
.addStatement("super(glide, lifecycle, treeNode)")
133+
.addParameter(CONTEXT_CLASS_NAME, "context")
134+
.addStatement("super(glide, lifecycle, treeNode, context)")
132135
.build();
133136
}
134137

@@ -150,7 +153,7 @@ private MethodSpec generateAsMethod(String generatedCodePackageName, TypeSpec re
150153
.addParameter(classOfResouceType, "resourceClass")
151154
.addAnnotation(AnnotationSpec.builder(CHECK_RESULT_CLASS_NAME).build())
152155
.returns(requestBuilderOfResourceType)
153-
.addStatement("return new $T<>(glide, this, resourceClass)",
156+
.addStatement("return new $T<>(glide, this, resourceClass, context)",
154157
this.generatedRequestBuilderClassName)
155158
.build();
156159
}

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

+12-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static com.bumptech.glide.request.RequestOptions.signatureOf;
44

5+
import android.content.Context;
56
import android.net.Uri;
67
import android.support.annotation.CheckResult;
78
import android.support.annotation.DrawableRes;
@@ -41,11 +42,12 @@ public class RequestBuilder<TranscodeType> implements Cloneable {
4142
new RequestOptions().diskCacheStrategy(DiskCacheStrategy.DATA).priority(Priority.LOW)
4243
.skipMemoryCache(true);
4344

44-
private final GlideContext context;
45+
private final Context context;
4546
private final RequestManager requestManager;
4647
private final Class<TranscodeType> transcodeClass;
4748
private final RequestOptions defaultRequestOptions;
4849
private final Glide glide;
50+
private final GlideContext glideContext;
4951

5052
@NonNull protected RequestOptions requestOptions;
5153

@@ -65,18 +67,19 @@ public class RequestBuilder<TranscodeType> implements Cloneable {
6567
private boolean isThumbnailBuilt;
6668

6769
protected RequestBuilder(Glide glide, RequestManager requestManager,
68-
Class<TranscodeType> transcodeClass) {
70+
Class<TranscodeType> transcodeClass, Context context) {
6971
this.glide = glide;
7072
this.requestManager = requestManager;
71-
this.context = glide.getGlideContext();
7273
this.transcodeClass = transcodeClass;
7374
this.defaultRequestOptions = requestManager.getDefaultRequestOptions();
75+
this.context = context;
7476
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
7577
this.requestOptions = defaultRequestOptions;
78+
this.glideContext = glide.getGlideContext();
7679
}
7780

7881
protected RequestBuilder(Class<TranscodeType> transcodeClass, RequestBuilder<?> other) {
79-
this(other.glide, other.requestManager, transcodeClass);
82+
this(other.glide, other.requestManager, transcodeClass, other.context);
8083
model = other.model;
8184
isModelSet = other.isModelSet;
8285
requestOptions = other.requestOptions;
@@ -554,7 +557,7 @@ public Target<TranscodeType> into(ImageView view) {
554557
}
555558
}
556559

557-
return into(context.buildImageViewTarget(view, transcodeClass), requestOptions);
560+
return into(glideContext.buildImageViewTarget(view, transcodeClass), requestOptions);
558561
}
559562

560563
/**
@@ -607,10 +610,10 @@ public FutureTarget<TranscodeType> submit() {
607610
*/
608611
public FutureTarget<TranscodeType> submit(int width, int height) {
609612
final RequestFutureTarget<TranscodeType> target =
610-
new RequestFutureTarget<>(context.getMainHandler(), width, height);
613+
new RequestFutureTarget<>(glideContext.getMainHandler(), width, height);
611614

612615
if (Util.isOnBackgroundThread()) {
613-
context.getMainHandler().post(new Runnable() {
616+
glideContext.getMainHandler().post(new Runnable() {
614617
@Override
615618
public void run() {
616619
if (!target.isCancelled()) {
@@ -839,6 +842,7 @@ private Request obtainRequest(Target<TranscodeType> target,
839842
int overrideWidth, int overrideHeight) {
840843
return SingleRequest.obtain(
841844
context,
845+
glideContext,
842846
model,
843847
transcodeClass,
844848
requestOptions,
@@ -848,7 +852,7 @@ private Request obtainRequest(Target<TranscodeType> target,
848852
target,
849853
requestListener,
850854
requestCoordinator,
851-
context.getEngine(),
855+
glideContext.getEngine(),
852856
transitionOptions.getTransitionFactory());
853857
}
854858
}

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

+17-7
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class RequestManager implements LifecycleListener {
5151
.skipMemoryCache(true);
5252

5353
protected final Glide glide;
54+
protected final Context context;
5455
@Synthetic final Lifecycle lifecycle;
5556
private final RequestTracker requestTracker;
5657
private final RequestManagerTreeNode treeNode;
@@ -67,8 +68,15 @@ public void run() {
6768
@NonNull
6869
private RequestOptions requestOptions;
6970

70-
public RequestManager(Glide glide, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
71-
this(glide, lifecycle, treeNode, new RequestTracker(), glide.getConnectivityMonitorFactory());
71+
public RequestManager(
72+
Glide glide, Lifecycle lifecycle, RequestManagerTreeNode treeNode, Context context) {
73+
this(
74+
glide,
75+
lifecycle,
76+
treeNode,
77+
new RequestTracker(),
78+
glide.getConnectivityMonitorFactory(),
79+
context);
7280
}
7381

7482
// Our usage is safe here.
@@ -78,16 +86,18 @@ public RequestManager(Glide glide, Lifecycle lifecycle, RequestManagerTreeNode t
7886
Lifecycle lifecycle,
7987
RequestManagerTreeNode treeNode,
8088
RequestTracker requestTracker,
81-
ConnectivityMonitorFactory factory) {
89+
ConnectivityMonitorFactory factory,
90+
Context context) {
8291
this.glide = glide;
8392
this.lifecycle = lifecycle;
8493
this.treeNode = treeNode;
8594
this.requestTracker = requestTracker;
86-
87-
final Context context = glide.getGlideContext().getBaseContext();
95+
this.context = context;
8896

8997
connectivityMonitor =
90-
factory.build(context, new RequestManagerConnectivityListener(requestTracker));
98+
factory.build(
99+
context.getApplicationContext(),
100+
new RequestManagerConnectivityListener(requestTracker));
91101

92102
// If we're the application level request manager, we may be created on a background thread.
93103
// In that case we cannot risk synchronously pausing or resuming requests, so we hack around the
@@ -393,7 +403,7 @@ public RequestBuilder<File> asFile() {
393403
*/
394404
@CheckResult
395405
public <ResourceType> RequestBuilder<ResourceType> as(Class<ResourceType> resourceClass) {
396-
return new RequestBuilder<>(glide, this, resourceClass);
406+
return new RequestBuilder<>(glide, this, resourceClass, context);
397407
}
398408

399409
/**

library/src/main/java/com/bumptech/glide/manager/RequestManagerRetriever.java

+16-7
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,13 @@ private RequestManager getApplicationManager(Context context) {
9090
// ApplicationLifecycle.
9191

9292
// TODO(b/27524013): Factor out this Glide.get() call.
93-
Glide glide = Glide.get(context);
93+
Glide glide = Glide.get(context.getApplicationContext());
9494
applicationManager =
95-
factory.build(glide, new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
95+
factory.build(
96+
glide,
97+
new ApplicationLifecycle(),
98+
new EmptyRequestManagerTreeNode(),
99+
context.getApplicationContext());
96100
}
97101
}
98102
}
@@ -338,7 +342,8 @@ private RequestManager fragmentGet(Context context, android.app.FragmentManager
338342
// TODO(b/27524013): Factor out this Glide.get() call.
339343
Glide glide = Glide.get(context);
340344
requestManager =
341-
factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode());
345+
factory.build(
346+
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
342347
current.setRequestManager(requestManager);
343348
}
344349
return requestManager;
@@ -369,7 +374,8 @@ private RequestManager supportFragmentGet(Context context, FragmentManager fm,
369374
// TODO(b/27524013): Factor out this Glide.get() call.
370375
Glide glide = Glide.get(context);
371376
requestManager =
372-
factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode());
377+
factory.build(
378+
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
373379
current.setRequestManager(requestManager);
374380
}
375381
return requestManager;
@@ -406,14 +412,17 @@ public boolean handleMessage(Message message) {
406412
*/
407413
public interface RequestManagerFactory {
408414
RequestManager build(
409-
Glide glide, Lifecycle lifecycle, RequestManagerTreeNode requestManagerTreeNode);
415+
Glide glide,
416+
Lifecycle lifecycle,
417+
RequestManagerTreeNode requestManagerTreeNode,
418+
Context context);
410419
}
411420

412421
private static final RequestManagerFactory DEFAULT_FACTORY = new RequestManagerFactory() {
413422
@Override
414423
public RequestManager build(Glide glide, Lifecycle lifecycle,
415-
RequestManagerTreeNode requestManagerTreeNode) {
416-
return new RequestManager(glide, lifecycle, requestManagerTreeNode);
424+
RequestManagerTreeNode requestManagerTreeNode, Context context) {
425+
return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);
417426
}
418427
};
419428
}

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

+10
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,16 @@ public RequestOptions error(@DrawableRes int resourceId) {
653653
* for resource ids provided via {@link #error(int)}, {@link #placeholder(int)}, and
654654
* {@link #fallback(Drawable)}.
655655
*
656+
* <p>The theme is <em>NOT</em> applied in the decoder that will attempt to decode a given
657+
* resource id model on Glide's background threads. The theme is used exclusively on the main
658+
* thread to obtain placeholder/error/fallback drawables to avoid leaking Activities.
659+
*
660+
* <p>If the {@link android.content.Context} of the {@link android.app.Fragment} or
661+
* {@link android.app.Activity} used to start this load has a different
662+
* {@link android.content.res.Resources.Theme}, the {@link android.content.res.Resources.Theme}
663+
* provided here will override the {@link android.content.res.Resources.Theme} of the
664+
* {@link android.content.Context}.
665+
*
656666
* @param theme The theme to use when loading Drawables.
657667
* @return this request builder.
658668
*/

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

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.bumptech.glide.request;
22

3+
import android.content.Context;
4+
import android.content.res.Resources.Theme;
35
import android.graphics.drawable.Drawable;
46
import android.support.annotation.DrawableRes;
57
import android.support.annotation.Nullable;
@@ -84,6 +86,7 @@ private enum Status {
8486
private final StateVerifier stateVerifier = StateVerifier.newInstance();
8587

8688
private RequestCoordinator requestCoordinator;
89+
private Context context;
8790
private GlideContext glideContext;
8891
@Nullable
8992
private Object model;
@@ -107,6 +110,7 @@ private enum Status {
107110
private int height;
108111

109112
public static <R> SingleRequest<R> obtain(
113+
Context context,
110114
GlideContext glideContext,
111115
Object model,
112116
Class<R> transcodeClass,
@@ -125,6 +129,7 @@ public static <R> SingleRequest<R> obtain(
125129
request = new SingleRequest<>();
126130
}
127131
request.init(
132+
context,
128133
glideContext,
129134
model,
130135
transcodeClass,
@@ -146,6 +151,7 @@ public static <R> SingleRequest<R> obtain(
146151
}
147152

148153
private void init(
154+
Context context,
149155
GlideContext glideContext,
150156
Object model,
151157
Class<R> transcodeClass,
@@ -158,6 +164,7 @@ private void init(
158164
RequestCoordinator requestCoordinator,
159165
Engine engine,
160166
TransitionFactory<? super R> animationFactory) {
167+
this.context = context;
161168
this.glideContext = glideContext;
162169
this.model = model;
163170
this.transcodeClass = transcodeClass;
@@ -181,6 +188,7 @@ public StateVerifier getVerifier() {
181188
@Override
182189
public void recycle() {
183190
assertNotCallingCallbacks();
191+
context = null;
184192
glideContext = null;
185193
model = null;
186194
transcodeClass = null;
@@ -379,7 +387,9 @@ private Drawable getFallbackDrawable() {
379387
}
380388

381389
private Drawable loadDrawable(@DrawableRes int resourceId) {
382-
return DrawableDecoderCompat.getDrawable(glideContext, resourceId, requestOptions.getTheme());
390+
Theme theme = requestOptions.getTheme() != null
391+
? requestOptions.getTheme() : context.getTheme();
392+
return DrawableDecoderCompat.getDrawable(glideContext, resourceId, theme);
383393
}
384394

385395
private void setErrorPlaceholder() {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public Boolean answer(InvocationOnMock invocation) throws Throwable {
127127

128128
Lifecycle lifecycle = mock(Lifecycle.class);
129129
RequestManagerTreeNode treeNode = mock(RequestManagerTreeNode.class);
130-
requestManager = new RequestManager(Glide.get(getContext()), lifecycle, treeNode);
130+
requestManager = new RequestManager(Glide.get(getContext()), lifecycle, treeNode, getContext());
131131
requestManager.resumeRequests();
132132
}
133133

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

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import static org.mockito.Mockito.verify;
99
import static org.mockito.Mockito.when;
1010

11+
import android.app.Application;
1112
import android.widget.ImageView;
1213
import com.bumptech.glide.request.Request;
1314
import com.bumptech.glide.request.RequestOptions;
@@ -30,11 +31,13 @@ public class RequestBuilderTest {
3031
@Mock GlideContext glideContext;
3132
@Mock RequestManager requestManager;
3233
private Glide glide;
34+
private Application context;
3335

3436
@Before
3537
public void setUp() {
3638
MockitoAnnotations.initMocks(this);
3739
glide = Glide.get(RuntimeEnvironment.application);
40+
context = RuntimeEnvironment.application;
3841
}
3942

4043
@After
@@ -44,7 +47,7 @@ public void tearDown() {
4447

4548
@Test(expected = NullPointerException.class)
4649
public void testThrowsIfContextIsNull() {
47-
new RequestBuilder<>(null /*context*/, requestManager, Object.class);
50+
new RequestBuilder<>(null /*context*/, requestManager, Object.class, context);
4851
}
4952

5053
@Test(expected = NullPointerException.class)
@@ -119,7 +122,7 @@ private RequestBuilder<Object> getNullModelRequest() {
119122
.thenReturn(new RequestOptions());
120123
when(requestManager.getDefaultTransitionOptions(any(Class.class)))
121124
.thenReturn(new GenericTransitionOptions<>());
122-
return new RequestBuilder<>(glide, requestManager, Object.class)
125+
return new RequestBuilder<>(glide, requestManager, Object.class, context)
123126
.load((Object) null);
124127
}
125128
}

0 commit comments

Comments
 (0)