From 31b501d1955a7bbc1d3a6ed92307a2aa8c43e392 Mon Sep 17 00:00:00 2001 From: Daniel Tomasiewicz Date: Fri, 19 Jul 2019 14:12:05 -0700 Subject: [PATCH] Stop unwrapping Contexts if the base has a null application Context. The Context#createPackageContext API returns Context without an Application instance attached, causing #getApplicationContext to return null. If this is overridden to a non-null value using a ContextWrapper, stop unwrapping so that code relying on #getApplicationContext will still work. PiperOrigin-RevId: 259036178 --- .../manager/RequestManagerRetriever.java | 6 ++- library/test/build.gradle | 1 + .../manager/RequestManagerRetrieverTest.java | 44 +++++++++++++++---- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/library/src/main/java/com/bumptech/glide/manager/RequestManagerRetriever.java b/library/src/main/java/com/bumptech/glide/manager/RequestManagerRetriever.java index d1b6cffeba..71aeaed473 100644 --- a/library/src/main/java/com/bumptech/glide/manager/RequestManagerRetriever.java +++ b/library/src/main/java/com/bumptech/glide/manager/RequestManagerRetriever.java @@ -108,7 +108,11 @@ public RequestManager get(@NonNull Context context) { return get((FragmentActivity) context); } else if (context instanceof Activity) { return get((Activity) context); - } else if (context instanceof ContextWrapper) { + } else if (context instanceof ContextWrapper + // Only unwrap a ContextWrapper if the baseContext has a non-null application context. + // Context#createPackageContext may return a Context without an Application instance, + // in which case a ContextWrapper may be used to attach one. + && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) { return get(((ContextWrapper) context).getBaseContext()); } } diff --git a/library/test/build.gradle b/library/test/build.gradle index 47e87df5b6..41f86f4410 100644 --- a/library/test/build.gradle +++ b/library/test/build.gradle @@ -11,6 +11,7 @@ dependencies { testImplementation "org.robolectric:robolectric:${ROBOLECTRIC_VERSION}" testImplementation "com.squareup.okhttp3:mockwebserver:${MOCKWEBSERVER_VERSION}" testImplementation "androidx.legacy:legacy-support-v4:${ANDROID_X_VERSION}" + testImplementation "androidx.test:core:${ANDROID_X_VERSION}" } tasks.withType(JavaCompile) { diff --git a/library/test/src/test/java/com/bumptech/glide/manager/RequestManagerRetrieverTest.java b/library/test/src/test/java/com/bumptech/glide/manager/RequestManagerRetrieverTest.java index 565679b2d1..48da49a2e8 100644 --- a/library/test/src/test/java/com/bumptech/glide/manager/RequestManagerRetrieverTest.java +++ b/library/test/src/test/java/com/bumptech/glide/manager/RequestManagerRetrieverTest.java @@ -17,6 +17,7 @@ import androidx.annotation.RequiresApi; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; +import androidx.test.core.app.ApplicationProvider; import com.bumptech.glide.RequestManager; import com.bumptech.glide.tests.BackgroundUtil.BackgroundTester; import com.bumptech.glide.tests.GlideShadowLooper; @@ -30,7 +31,6 @@ import org.mockito.Mockito; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.Shadows; import org.robolectric.android.controller.ActivityController; import org.robolectric.annotation.Config; @@ -41,12 +41,15 @@ public class RequestManagerRetrieverTest { @Rule public TearDownGlide tearDownGlide = new TearDownGlide(); private static final String PARENT_TAG = "parent"; + private Context appContext; private RetrieverHarness[] harnesses; private RequestManagerRetriever retriever; private int initialSdkVersion; @Before public void setUp() { + appContext = ApplicationProvider.getApplicationContext(); + retriever = new RequestManagerRetriever(null /*factory*/); harnesses = @@ -271,28 +274,53 @@ public void testHandlesContextWrappersForActivities() { @Test public void testHandlesContextWrappersForApplication() { - ContextWrapper contextWrapper = new ContextWrapper(RuntimeEnvironment.application); - RequestManager requestManager = retriever.get(RuntimeEnvironment.application); + ContextWrapper contextWrapper = new ContextWrapper(appContext); + RequestManager requestManager = retriever.get(appContext); assertEquals(requestManager, retriever.get(contextWrapper)); } + @Test + public void testHandlesContextWrapperWithoutApplication() throws Exception { + // Create a Context which is not associated with an Application instance. + Context baseContext = + appContext.createPackageContext(appContext.getPackageName(), /*flags=*/ 0); + + // Sanity-check that Robolectric behaves the same as the framework. + assertThat(baseContext.getApplicationContext()).isNull(); + + // If a wrapper provides a non-null application Context, unwrapping should terminate at this + // wrapper so that the returned Context has a non-null #getApplicationContext. + Context contextWithApplicationContext = + new ContextWrapper(baseContext) { + @Override + public Context getApplicationContext() { + return this; + } + }; + + Context wrappedContext = new ContextWrapper(contextWithApplicationContext); + RequestManager requestManager = retriever.get(appContext); + + assertEquals(requestManager, retriever.get(wrappedContext)); + } + @Test public void testReturnsNonNullManagerIfGivenApplicationContext() { - assertNotNull(retriever.get(RuntimeEnvironment.application)); + assertNotNull(retriever.get(appContext)); } @Test public void testApplicationRequestManagerIsNotPausedWhenRetrieved() { - RequestManager manager = retriever.get(RuntimeEnvironment.application); + RequestManager manager = retriever.get(appContext); assertFalse(manager.isPaused()); } @Test public void testApplicationRequestManagerIsNotReResumedAfterFirstRetrieval() { - RequestManager manager = retriever.get(RuntimeEnvironment.application); + RequestManager manager = retriever.get(appContext); manager.pauseRequests(); - manager = retriever.get(RuntimeEnvironment.application); + manager = retriever.get(appContext); assertTrue(manager.isPaused()); } @@ -303,7 +331,7 @@ public void testDoesNotThrowWhenGetWithContextCalledFromBackgroundThread() new BackgroundTester() { @Override public void runTest() { - retriever.get(RuntimeEnvironment.application); + retriever.get(appContext); } }); }