提交 bbb25aff 编写于 作者: S Sam Judd

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.
上级 702386e3
......@@ -359,6 +359,7 @@ final class RequestBuilderGenerator {
.addStatement("super($N, $N)", "transcodeClass", "other")
.build();
ClassName context = ClassName.get("android.content", "Context");
ClassName glide = ClassName.get("com.bumptech.glide", "Glide");
ClassName requestManager = ClassName.get("com.bumptech.glide", "RequestManager");
MethodSpec secondConstructor =
......@@ -366,7 +367,9 @@ final class RequestBuilderGenerator {
.addParameter(glide, "glide")
.addParameter(requestManager, "requestManager")
.addParameter(classOfTranscodeType, "transcodeClass")
.addStatement("super($N, $N ,$N)", "glide", "requestManager", "transcodeClass")
.addParameter(context, "context")
.addStatement(
"super($N, $N ,$N, $N)", "glide", "requestManager", "transcodeClass", "context")
.build();
return ImmutableList.of(firstConstructor, secondConstructor);
}
......
......@@ -39,6 +39,8 @@ final class RequestManagerFactoryGenerator {
"com.bumptech.glide.manager.RequestManagerRetriever.RequestManagerFactory";
private static final String REQUEST_MANAGER_QUALIFIED_NAME =
"com.bumptech.glide.RequestManager";
private static final ClassName CONTEXT_CLASS_NAME =
ClassName.get("android.content", "Context");
static final String GENERATED_REQUEST_MANAGER_FACTORY_PACKAGE_NAME =
"com.bumptech.glide";
......@@ -79,8 +81,9 @@ final class RequestManagerFactoryGenerator {
.addParameter(ClassName.get(glideType), "glide")
.addParameter(ClassName.get(lifecycleType), "lifecycle")
.addParameter(ClassName.get(requestManagerTreeNodeType), "treeNode")
.addParameter(CONTEXT_CLASS_NAME, "context")
.addStatement(
"return new $T(glide, lifecycle, treeNode)",
"return new $T(glide, lifecycle, treeNode, context)",
ClassName.get(generatedCodePackageName, generatedRequestManagerSpec.name))
.build()
)
......
......@@ -59,6 +59,8 @@ final class RequestManagerGenerator {
"com.bumptech.glide.manager.RequestManagerTreeNode";
private static final ClassName CHECK_RESULT_CLASS_NAME =
ClassName.get("android.support.annotation", "CheckResult");
private static final ClassName CONTEXT_CLASS_NAME =
ClassName.get("android.content", "Context");
private static final String GENERATED_REQUEST_MANAGER_SIMPLE_NAME =
"GlideRequests";
......@@ -128,7 +130,8 @@ final class RequestManagerGenerator {
.addParameter(ClassName.get(glideType), "glide")
.addParameter(ClassName.get(lifecycleType), "lifecycle")
.addParameter(ClassName.get(requestManagerTreeNodeType), "treeNode")
.addStatement("super(glide, lifecycle, treeNode)")
.addParameter(CONTEXT_CLASS_NAME, "context")
.addStatement("super(glide, lifecycle, treeNode, context)")
.build();
}
......@@ -150,7 +153,7 @@ final class RequestManagerGenerator {
.addParameter(classOfResouceType, "resourceClass")
.addAnnotation(AnnotationSpec.builder(CHECK_RESULT_CLASS_NAME).build())
.returns(requestBuilderOfResourceType)
.addStatement("return new $T<>(glide, this, resourceClass)",
.addStatement("return new $T<>(glide, this, resourceClass, context)",
this.generatedRequestBuilderClassName)
.build();
}
......
......@@ -2,6 +2,7 @@ package com.bumptech.glide;
import static com.bumptech.glide.request.RequestOptions.signatureOf;
import android.content.Context;
import android.net.Uri;
import android.support.annotation.CheckResult;
import android.support.annotation.DrawableRes;
......@@ -41,11 +42,12 @@ public class RequestBuilder<TranscodeType> implements Cloneable {
new RequestOptions().diskCacheStrategy(DiskCacheStrategy.DATA).priority(Priority.LOW)
.skipMemoryCache(true);
private final GlideContext context;
private final Context context;
private final RequestManager requestManager;
private final Class<TranscodeType> transcodeClass;
private final RequestOptions defaultRequestOptions;
private final Glide glide;
private final GlideContext glideContext;
@NonNull protected RequestOptions requestOptions;
......@@ -65,18 +67,19 @@ public class RequestBuilder<TranscodeType> implements Cloneable {
private boolean isThumbnailBuilt;
protected RequestBuilder(Glide glide, RequestManager requestManager,
Class<TranscodeType> transcodeClass) {
Class<TranscodeType> transcodeClass, Context context) {
this.glide = glide;
this.requestManager = requestManager;
this.context = glide.getGlideContext();
this.transcodeClass = transcodeClass;
this.defaultRequestOptions = requestManager.getDefaultRequestOptions();
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.requestOptions = defaultRequestOptions;
this.glideContext = glide.getGlideContext();
}
protected RequestBuilder(Class<TranscodeType> transcodeClass, RequestBuilder<?> other) {
this(other.glide, other.requestManager, transcodeClass);
this(other.glide, other.requestManager, transcodeClass, other.context);
model = other.model;
isModelSet = other.isModelSet;
requestOptions = other.requestOptions;
......@@ -554,7 +557,7 @@ public class RequestBuilder<TranscodeType> implements Cloneable {
}
}
return into(context.buildImageViewTarget(view, transcodeClass), requestOptions);
return into(glideContext.buildImageViewTarget(view, transcodeClass), requestOptions);
}
/**
......@@ -607,10 +610,10 @@ public class RequestBuilder<TranscodeType> implements Cloneable {
*/
public FutureTarget<TranscodeType> submit(int width, int height) {
final RequestFutureTarget<TranscodeType> target =
new RequestFutureTarget<>(context.getMainHandler(), width, height);
new RequestFutureTarget<>(glideContext.getMainHandler(), width, height);
if (Util.isOnBackgroundThread()) {
context.getMainHandler().post(new Runnable() {
glideContext.getMainHandler().post(new Runnable() {
@Override
public void run() {
if (!target.isCancelled()) {
......@@ -839,6 +842,7 @@ public class RequestBuilder<TranscodeType> implements Cloneable {
int overrideWidth, int overrideHeight) {
return SingleRequest.obtain(
context,
glideContext,
model,
transcodeClass,
requestOptions,
......@@ -848,7 +852,7 @@ public class RequestBuilder<TranscodeType> implements Cloneable {
target,
requestListener,
requestCoordinator,
context.getEngine(),
glideContext.getEngine(),
transitionOptions.getTransitionFactory());
}
}
......@@ -51,6 +51,7 @@ public class RequestManager implements LifecycleListener {
.skipMemoryCache(true);
protected final Glide glide;
protected final Context context;
@Synthetic final Lifecycle lifecycle;
private final RequestTracker requestTracker;
private final RequestManagerTreeNode treeNode;
......@@ -67,8 +68,15 @@ public class RequestManager implements LifecycleListener {
@NonNull
private RequestOptions requestOptions;
public RequestManager(Glide glide, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
this(glide, lifecycle, treeNode, new RequestTracker(), glide.getConnectivityMonitorFactory());
public RequestManager(
Glide glide, Lifecycle lifecycle, RequestManagerTreeNode treeNode, Context context) {
this(
glide,
lifecycle,
treeNode,
new RequestTracker(),
glide.getConnectivityMonitorFactory(),
context);
}
// Our usage is safe here.
......@@ -78,16 +86,18 @@ public class RequestManager implements LifecycleListener {
Lifecycle lifecycle,
RequestManagerTreeNode treeNode,
RequestTracker requestTracker,
ConnectivityMonitorFactory factory) {
ConnectivityMonitorFactory factory,
Context context) {
this.glide = glide;
this.lifecycle = lifecycle;
this.treeNode = treeNode;
this.requestTracker = requestTracker;
final Context context = glide.getGlideContext().getBaseContext();
this.context = context;
connectivityMonitor =
factory.build(context, new RequestManagerConnectivityListener(requestTracker));
factory.build(
context.getApplicationContext(),
new RequestManagerConnectivityListener(requestTracker));
// If we're the application level request manager, we may be created on a background thread.
// In that case we cannot risk synchronously pausing or resuming requests, so we hack around the
......@@ -393,7 +403,7 @@ public class RequestManager implements LifecycleListener {
*/
@CheckResult
public <ResourceType> RequestBuilder<ResourceType> as(Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass);
return new RequestBuilder<>(glide, this, resourceClass, context);
}
/**
......
......@@ -90,9 +90,13 @@ public class RequestManagerRetriever implements Handler.Callback {
// ApplicationLifecycle.
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
Glide glide = Glide.get(context.getApplicationContext());
applicationManager =
factory.build(glide, new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
factory.build(
glide,
new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
}
}
}
......@@ -338,7 +342,8 @@ public class RequestManagerRetriever implements Handler.Callback {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode());
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
......@@ -369,7 +374,8 @@ public class RequestManagerRetriever implements Handler.Callback {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode());
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
......@@ -406,14 +412,17 @@ public class RequestManagerRetriever implements Handler.Callback {
*/
public interface RequestManagerFactory {
RequestManager build(
Glide glide, Lifecycle lifecycle, RequestManagerTreeNode requestManagerTreeNode);
Glide glide,
Lifecycle lifecycle,
RequestManagerTreeNode requestManagerTreeNode,
Context context);
}
private static final RequestManagerFactory DEFAULT_FACTORY = new RequestManagerFactory() {
@Override
public RequestManager build(Glide glide, Lifecycle lifecycle,
RequestManagerTreeNode requestManagerTreeNode) {
return new RequestManager(glide, lifecycle, requestManagerTreeNode);
RequestManagerTreeNode requestManagerTreeNode, Context context) {
return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);
}
};
}
......@@ -653,6 +653,16 @@ public class RequestOptions implements Cloneable {
* for resource ids provided via {@link #error(int)}, {@link #placeholder(int)}, and
* {@link #fallback(Drawable)}.
*
* <p>The theme is <em>NOT</em> applied in the decoder that will attempt to decode a given
* resource id model on Glide's background threads. The theme is used exclusively on the main
* thread to obtain placeholder/error/fallback drawables to avoid leaking Activities.
*
* <p>If the {@link android.content.Context} of the {@link android.app.Fragment} or
* {@link android.app.Activity} used to start this load has a different
* {@link android.content.res.Resources.Theme}, the {@link android.content.res.Resources.Theme}
* provided here will override the {@link android.content.res.Resources.Theme} of the
* {@link android.content.Context}.
*
* @param theme The theme to use when loading Drawables.
* @return this request builder.
*/
......
package com.bumptech.glide.request;
import android.content.Context;
import android.content.res.Resources.Theme;
import android.graphics.drawable.Drawable;
import android.support.annotation.DrawableRes;
import android.support.annotation.Nullable;
......@@ -84,6 +86,7 @@ public final class SingleRequest<R> implements Request,
private final StateVerifier stateVerifier = StateVerifier.newInstance();
private RequestCoordinator requestCoordinator;
private Context context;
private GlideContext glideContext;
@Nullable
private Object model;
......@@ -107,6 +110,7 @@ public final class SingleRequest<R> implements Request,
private int height;
public static <R> SingleRequest<R> obtain(
Context context,
GlideContext glideContext,
Object model,
Class<R> transcodeClass,
......@@ -125,6 +129,7 @@ public final class SingleRequest<R> implements Request,
request = new SingleRequest<>();
}
request.init(
context,
glideContext,
model,
transcodeClass,
......@@ -146,6 +151,7 @@ public final class SingleRequest<R> implements Request,
}
private void init(
Context context,
GlideContext glideContext,
Object model,
Class<R> transcodeClass,
......@@ -158,6 +164,7 @@ public final class SingleRequest<R> implements Request,
RequestCoordinator requestCoordinator,
Engine engine,
TransitionFactory<? super R> animationFactory) {
this.context = context;
this.glideContext = glideContext;
this.model = model;
this.transcodeClass = transcodeClass;
......@@ -181,6 +188,7 @@ public final class SingleRequest<R> implements Request,
@Override
public void recycle() {
assertNotCallingCallbacks();
context = null;
glideContext = null;
model = null;
transcodeClass = null;
......@@ -379,7 +387,9 @@ public final class SingleRequest<R> implements Request,
}
private Drawable loadDrawable(@DrawableRes int resourceId) {
return DrawableDecoderCompat.getDrawable(glideContext, resourceId, requestOptions.getTheme());
Theme theme = requestOptions.getTheme() != null
? requestOptions.getTheme() : context.getTheme();
return DrawableDecoderCompat.getDrawable(glideContext, resourceId, theme);
}
private void setErrorPlaceholder() {
......
......@@ -127,7 +127,7 @@ public class GlideTest {
Lifecycle lifecycle = mock(Lifecycle.class);
RequestManagerTreeNode treeNode = mock(RequestManagerTreeNode.class);
requestManager = new RequestManager(Glide.get(getContext()), lifecycle, treeNode);
requestManager = new RequestManager(Glide.get(getContext()), lifecycle, treeNode, getContext());
requestManager.resumeRequests();
}
......
......@@ -8,6 +8,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Application;
import android.widget.ImageView;
import com.bumptech.glide.request.Request;
import com.bumptech.glide.request.RequestOptions;
......@@ -30,11 +31,13 @@ public class RequestBuilderTest {
@Mock GlideContext glideContext;
@Mock RequestManager requestManager;
private Glide glide;
private Application context;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
glide = Glide.get(RuntimeEnvironment.application);
context = RuntimeEnvironment.application;
}
@After
......@@ -44,7 +47,7 @@ public class RequestBuilderTest {
@Test(expected = NullPointerException.class)
public void testThrowsIfContextIsNull() {
new RequestBuilder<>(null /*context*/, requestManager, Object.class);
new RequestBuilder<>(null /*context*/, requestManager, Object.class, context);
}
@Test(expected = NullPointerException.class)
......@@ -119,7 +122,7 @@ public class RequestBuilderTest {
.thenReturn(new RequestOptions());
when(requestManager.getDefaultTransitionOptions(any(Class.class)))
.thenReturn(new GenericTransitionOptions<>());
return new RequestBuilder<>(glide, requestManager, Object.class)
return new RequestBuilder<>(glide, requestManager, Object.class, context)
.load((Object) null);
}
}
......@@ -44,6 +44,7 @@ public class RequestManagerTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = RuntimeEnvironment.application;
connectivityMonitor = mock(ConnectivityMonitor.class);
ConnectivityMonitorFactory factory = mock(ConnectivityMonitorFactory.class);
when(factory.build(isA(Context.class), isA(ConnectivityMonitor.ConnectivityListener.class)))
......@@ -61,7 +62,8 @@ public class RequestManagerTest {
lifecycle,
treeNode,
requestTracker,
factory);
factory,
context);
}
@Test
......
......@@ -97,7 +97,7 @@ public class SingleRequestTest {
.signature(signature)
.useUnlimitedSourceGeneratorsPool(useUnlimitedSourceGeneratorsPool);
return SingleRequest
.obtain(glideContext, model, transcodeClass, requestOptions, overrideWidth,
.obtain(glideContext, glideContext, model, transcodeClass, requestOptions, overrideWidth,
overrideHeight, priority, target, requestListener, requestCoordinator, engine,
factory);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册