From e6a4ea3642159d62fc53389875e2ea890cc430b9 Mon Sep 17 00:00:00 2001
From: Sam Judd
Date: Wed, 21 Jan 2015 09:39:26 -0800
Subject: [PATCH] Add GlideModule for lazily initialization.
Fixes #299.
---
.../okhttp/src/main/AndroidManifest.xml | 7 +-
.../integration/okhttp/OkHttpGlideModule.java | 27 ++++
.../volley/src/main/AndroidManifest.xml | 6 +-
.../integration/volley/VolleyGlideModule.java | 27 ++++
.../glide/module/ManifestParserTest.java | 145 ++++++++++++++++++
.../main/java/com/bumptech/glide/Glide.java | 6 +
.../bumptech/glide/module/GlideModule.java | 57 +++++++
.../bumptech/glide/module/ManifestParser.java | 63 ++++++++
samples/flickr/src/main/AndroidManifest.xml | 3 +
.../samples/flickr/FlickrGlideModule.java | 19 +++
.../samples/flickr/FlickrSearchActivity.java | 2 -
samples/giphy/src/main/AndroidManifest.xml | 4 +
.../glide/samples/giphy/GiphyGlideModule.java | 18 +++
.../glide/samples/giphy/MainActivity.java | 8 +-
14 files changed, 381 insertions(+), 11 deletions(-)
create mode 100644 integration/okhttp/src/main/java/com/bumptech/glide/integration/okhttp/OkHttpGlideModule.java
create mode 100644 integration/volley/src/main/java/com/bumptech/glide/integration/volley/VolleyGlideModule.java
create mode 100644 library/src/androidTest/java/com/bumptech/glide/module/ManifestParserTest.java
create mode 100644 library/src/main/java/com/bumptech/glide/module/GlideModule.java
create mode 100644 library/src/main/java/com/bumptech/glide/module/ManifestParser.java
create mode 100644 samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrGlideModule.java
create mode 100644 samples/giphy/src/main/java/com/bumptech/glide/samples/giphy/GiphyGlideModule.java
diff --git a/integration/okhttp/src/main/AndroidManifest.xml b/integration/okhttp/src/main/AndroidManifest.xml
index 53b73913d..4ab340867 100644
--- a/integration/okhttp/src/main/AndroidManifest.xml
+++ b/integration/okhttp/src/main/AndroidManifest.xml
@@ -1,5 +1,8 @@
-
-
+
+
+
diff --git a/integration/okhttp/src/main/java/com/bumptech/glide/integration/okhttp/OkHttpGlideModule.java b/integration/okhttp/src/main/java/com/bumptech/glide/integration/okhttp/OkHttpGlideModule.java
new file mode 100644
index 000000000..1c780d879
--- /dev/null
+++ b/integration/okhttp/src/main/java/com/bumptech/glide/integration/okhttp/OkHttpGlideModule.java
@@ -0,0 +1,27 @@
+package com.bumptech.glide.integration.okhttp;
+
+import android.content.Context;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.model.GlideUrl;
+import com.bumptech.glide.module.GlideModule;
+
+import java.io.InputStream;
+
+/**
+ * A {@link com.bumptech.glide.module.GlideModule} implementation to replace Glide's default
+ * {@link java.net.HttpURLConnection} based {@link com.bumptech.glide.load.model.ModelLoader} with an OkHttp based
+ * {@link com.bumptech.glide.load.model.ModelLoader}.
+ *
+ *
+ * If you're using gradle, you can include this module simply by depending on the aar, the module will be merged
+ * in by manifest merger. For other build systems or for more more information, see
+ * {@link com.bumptech.glide.module.GlideModule}.
+ *
+ */
+public class OkHttpGlideModule implements GlideModule {
+ @Override
+ public void initialize(Context context, Glide glide) {
+ glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
+ }
+}
diff --git a/integration/volley/src/main/AndroidManifest.xml b/integration/volley/src/main/AndroidManifest.xml
index c2d679aab..554a22600 100644
--- a/integration/volley/src/main/AndroidManifest.xml
+++ b/integration/volley/src/main/AndroidManifest.xml
@@ -1,5 +1,9 @@
-
+
+
+
diff --git a/integration/volley/src/main/java/com/bumptech/glide/integration/volley/VolleyGlideModule.java b/integration/volley/src/main/java/com/bumptech/glide/integration/volley/VolleyGlideModule.java
new file mode 100644
index 000000000..5db09b365
--- /dev/null
+++ b/integration/volley/src/main/java/com/bumptech/glide/integration/volley/VolleyGlideModule.java
@@ -0,0 +1,27 @@
+package com.bumptech.glide.integration.volley;
+
+import android.content.Context;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.model.GlideUrl;
+import com.bumptech.glide.module.GlideModule;
+
+import java.io.InputStream;
+
+/**
+ * A {@link com.bumptech.glide.module.GlideModule} implementation to replace Glide's default
+ * {@link java.net.HttpURLConnection} based {@link com.bumptech.glide.load.model.ModelLoader} with a Volley based
+ * {@link com.bumptech.glide.load.model.ModelLoader}.
+ *
+ *
+ * If you're using gradle, you can include this module simply by depending on the aar, the module will be merged
+ * in by manifest merger. For other build systems or for more more information, see
+ * {@link com.bumptech.glide.module.GlideModule}.
+ *
+ */
+public class VolleyGlideModule implements GlideModule {
+ @Override
+ public void initialize(Context context, Glide glide) {
+ glide.register(GlideUrl.class, InputStream.class, new VolleyUrlLoader.Factory(context));
+ }
+}
diff --git a/library/src/androidTest/java/com/bumptech/glide/module/ManifestParserTest.java b/library/src/androidTest/java/com/bumptech/glide/module/ManifestParserTest.java
new file mode 100644
index 000000000..0e8914a1d
--- /dev/null
+++ b/library/src/androidTest/java/com/bumptech/glide/module/ManifestParserTest.java
@@ -0,0 +1,145 @@
+package com.bumptech.glide.module;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import com.bumptech.glide.Glide;
+
+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.annotation.Config;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = Config.NONE, emulateSdk = 18)
+public class ManifestParserTest {
+ private static final String MODULE_VALUE = "GlideModule";
+
+ @Mock Context context;
+ private ManifestParser parser;
+ private ApplicationInfo applicationInfo;
+
+ @Before
+ public void setUp() throws PackageManager.NameNotFoundException {
+ MockitoAnnotations.initMocks(this);
+ applicationInfo = new ApplicationInfo();
+ applicationInfo.metaData = new Bundle();
+
+ String packageName = "com.bumptech.test";
+ when(context.getPackageName()).thenReturn(packageName);
+
+ PackageManager pm = mock(PackageManager.class);
+ when(pm.getApplicationInfo(eq(packageName), eq(PackageManager.GET_META_DATA)))
+ .thenReturn(applicationInfo);
+ when(context.getPackageManager()).thenReturn(pm);
+
+ parser = new ManifestParser(context);
+ }
+
+ @Test
+ public void testParse_returnsEmptyListIfNoModulesListed() {
+ assertThat(parser.parse()).isEmpty();
+ }
+
+ @Test
+ public void testParse_withSingleValidModuleName_returnsListContainingModule() {
+ addModuleToManifest(TestModule1.class);
+
+ List modules = parser.parse();
+ assertThat(modules).hasSize(1);
+ assertThat(modules.get(0)).isInstanceOf(TestModule1.class);
+ }
+
+ @Test
+ public void testParse_withMultipleValidModuleNames_returnsListContainingModules() {
+ addModuleToManifest(TestModule1.class);
+ addModuleToManifest(TestModule2.class);
+
+ List modules = parser.parse();
+ assertThat(modules).hasSize(2);
+
+ assertThat(modules).contains(new TestModule1());
+ assertThat(modules).contains(new TestModule2());
+ }
+
+ @Test
+ public void testParse_withValidModuleName_ignoresMetadataWithoutGlideModuleValue() {
+ applicationInfo.metaData.putString(TestModule1.class.getName(), MODULE_VALUE + "test");
+
+ assertThat(parser.parse()).isEmpty();
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testThrows_whenModuleNameNotFound() {
+ addToManifest("fakeClassName");
+
+ parser.parse();
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testThrows_whenClassInManifestIsNotAModule() {
+ addModuleToManifest(InvalidClass.class);
+
+ parser.parse();
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testThrows_whenPackageNameNotFound() {
+ when(context.getPackageName()).thenReturn("fakePackageName");
+
+ parser.parse();
+ }
+
+ private void addModuleToManifest(Class> moduleClass) {
+ addToManifest(moduleClass.getName());
+ }
+
+ private void addToManifest(String key) {
+ applicationInfo.metaData.putString(key, MODULE_VALUE);
+ }
+
+ public static class InvalidClass { }
+
+ public static class TestModule1 implements GlideModule {
+ @Override
+ public void initialize(Context context, Glide glide) { }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof TestModule1;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+ }
+
+ public static class TestModule2 implements GlideModule {
+
+ @Override
+ public void initialize(Context context, Glide glide) { }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof TestModule2;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/bumptech/glide/Glide.java b/library/src/main/java/com/bumptech/glide/Glide.java
index 9461b2522..4c0569a9f 100644
--- a/library/src/main/java/com/bumptech/glide/Glide.java
+++ b/library/src/main/java/com/bumptech/glide/Glide.java
@@ -56,6 +56,8 @@ import com.bumptech.glide.load.resource.transcode.GlideBitmapDrawableTranscoder;
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
import com.bumptech.glide.load.resource.transcode.TranscoderRegistry;
import com.bumptech.glide.manager.RequestManagerRetriever;
+import com.bumptech.glide.module.GlideModule;
+import com.bumptech.glide.module.ManifestParser;
import com.bumptech.glide.provider.DataLoadProvider;
import com.bumptech.glide.provider.DataLoadProviderRegistry;
import com.bumptech.glide.request.FutureTarget;
@@ -241,6 +243,10 @@ public class Glide {
bitmapFitCenter = new FitCenter(bitmapPool);
drawableFitCenter = new GifBitmapWrapperTransformation(bitmapPool, bitmapFitCenter);
+
+ for (GlideModule module : new ManifestParser(context).parse()) {
+ module.initialize(context, this);
+ }
}
/**
diff --git a/library/src/main/java/com/bumptech/glide/module/GlideModule.java b/library/src/main/java/com/bumptech/glide/module/GlideModule.java
new file mode 100644
index 000000000..26217054f
--- /dev/null
+++ b/library/src/main/java/com/bumptech/glide/module/GlideModule.java
@@ -0,0 +1,57 @@
+package com.bumptech.glide.module;
+
+import android.content.Context;
+
+import com.bumptech.glide.Glide;
+
+/**
+ * An interface allowing lazy registration of {@link com.bumptech.glide.load.model.ModelLoader ModelLoaders}.
+ *
+ *
+ * To use this interface:
+ *
+ * -
+ * Implement the GlideModule interface in a class with public visibility, calling
+ * {@link com.bumptech.glide.Glide#register(Class, Class, com.bumptech.glide.load.model.ModelLoaderFactory)}
+ * for each {@link com.bumptech.glide.load.model.ModelLoader} you'd like to register:
+ *
+ *
+ * public class FlickrGlideModule implements GlideModule {
+ * {@literal @}Override
+ * public void initialize(Context context, Glide glide) {
+ * glide.register(Model.class, Data.class, new MyModelLoader());
+ * }
+ * }
+ *
+ *
+ *
+ * -
+ * Add your implementation to your list of keeps in your proguard.cfg file:
+ *
+ * {@code
+ * -keepnames class * com.bumptech.glide.samples.flickr.FlickrGlideModule
+ * }
+ *
+ *
+ * -
+ * Add a metadata tag to your AndroidManifest.xml with your GlideModule implementation's fully qualified
+ * classname as the key, and {@code GlideModule} as the value:
+ *
+ * {@code
+ *
+ * }
+ *
+ *
+ *
+ *
+ *
+ *
+ * All implementations must be publicly visible and contain only an empty constructor so they can be instantiated
+ * via reflection when Glide is lazily initialized.
+ *
+ */
+public interface GlideModule {
+ void initialize(Context context, Glide glide);
+}
diff --git a/library/src/main/java/com/bumptech/glide/module/ManifestParser.java b/library/src/main/java/com/bumptech/glide/module/ManifestParser.java
new file mode 100644
index 000000000..4419e03d7
--- /dev/null
+++ b/library/src/main/java/com/bumptech/glide/module/ManifestParser.java
@@ -0,0 +1,63 @@
+package com.bumptech.glide.module;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parses {@link com.bumptech.glide.module.GlideModule} references out of the AndroidManifest file.
+ */
+public final class ManifestParser {
+ private static final String GLIDE_MODULE_VALUE = "GlideModule";
+
+ private final Context context;
+
+ public ManifestParser(Context context) {
+ this.context = context;
+ }
+
+ public List parse() {
+ List modules = new ArrayList();
+ try {
+ ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
+ context.getPackageName(), PackageManager.GET_META_DATA);
+ if (appInfo.metaData != null) {
+ for (String key : appInfo.metaData.keySet()) {
+ if (GLIDE_MODULE_VALUE.equals(appInfo.metaData.getString(key))) {
+ modules.add(parseModule(key));
+ }
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException("Unable to find metadata to parse GlideModules", e);
+ }
+
+ return modules;
+ }
+
+ private static GlideModule parseModule(String className) {
+ Class> clazz;
+ try {
+ clazz = Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException("Unable to find GlideModule implementation", e);
+ }
+
+ Object module;
+ try {
+ module = clazz.newInstance();
+ } catch (InstantiationException e) {
+ throw new RuntimeException("Unable to instantiate GlideModule implementation for " + clazz, e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Unable to instantiate GlideModule implementation for " + clazz, e);
+ }
+
+ if (!(module instanceof GlideModule)) {
+ throw new RuntimeException("Expected instanceof GlideModule, but found: " + module);
+ }
+ return (GlideModule) module;
+ }
+}
diff --git a/samples/flickr/src/main/AndroidManifest.xml b/samples/flickr/src/main/AndroidManifest.xml
index 242d2cd15..338a135dc 100644
--- a/samples/flickr/src/main/AndroidManifest.xml
+++ b/samples/flickr/src/main/AndroidManifest.xml
@@ -23,6 +23,9 @@
+
diff --git a/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrGlideModule.java b/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrGlideModule.java
new file mode 100644
index 000000000..405acbb0b
--- /dev/null
+++ b/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrGlideModule.java
@@ -0,0 +1,19 @@
+package com.bumptech.glide.samples.flickr;
+
+import android.content.Context;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.module.GlideModule;
+import com.bumptech.glide.samples.flickr.api.Photo;
+
+import java.io.InputStream;
+
+/**
+ * {@link com.bumptech.glide.module.GlideModule} for the Flickr sample app.
+ */
+public class FlickrGlideModule implements GlideModule {
+ @Override
+ public void initialize(Context context, Glide glide) {
+ glide.register(Photo.class, InputStream.class, new FlickrModelLoader.Factory());
+ }
+}
diff --git a/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrSearchActivity.java b/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrSearchActivity.java
index 66fba9810..4b57a0e33 100644
--- a/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrSearchActivity.java
+++ b/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrSearchActivity.java
@@ -33,7 +33,6 @@ import com.bumptech.glide.samples.flickr.api.Api;
import com.bumptech.glide.samples.flickr.api.Photo;
import java.io.File;
-import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -90,7 +89,6 @@ public class FlickrSearchActivity extends ActionBarActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Glide.get(this).register(Photo.class, InputStream.class, new FlickrModelLoader.Factory());
backgroundThread = new HandlerThread("BackgroundThumbnailHandlerThread");
backgroundThread.start();
backgroundHandler = new Handler(backgroundThread.getLooper());
diff --git a/samples/giphy/src/main/AndroidManifest.xml b/samples/giphy/src/main/AndroidManifest.xml
index c5dbccb8f..5d353dcbc 100644
--- a/samples/giphy/src/main/AndroidManifest.xml
+++ b/samples/giphy/src/main/AndroidManifest.xml
@@ -19,6 +19,10 @@
+
+
diff --git a/samples/giphy/src/main/java/com/bumptech/glide/samples/giphy/GiphyGlideModule.java b/samples/giphy/src/main/java/com/bumptech/glide/samples/giphy/GiphyGlideModule.java
new file mode 100644
index 000000000..0e5844b2b
--- /dev/null
+++ b/samples/giphy/src/main/java/com/bumptech/glide/samples/giphy/GiphyGlideModule.java
@@ -0,0 +1,18 @@
+package com.bumptech.glide.samples.giphy;
+
+import android.content.Context;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.module.GlideModule;
+
+import java.io.InputStream;
+
+/**
+ * {@link com.bumptech.glide.module.GlideModule} implementation for the Giphy sample app.
+ */
+public class GiphyGlideModule implements GlideModule {
+ @Override
+ public void initialize(Context context, Glide glide) {
+ glide.register(Api.GifResult.class, InputStream.class, new GiphyModelLoader.Factory());
+ }
+}
diff --git a/samples/giphy/src/main/java/com/bumptech/glide/samples/giphy/MainActivity.java b/samples/giphy/src/main/java/com/bumptech/glide/samples/giphy/MainActivity.java
index e5dd70310..a99cbdc25 100644
--- a/samples/giphy/src/main/java/com/bumptech/glide/samples/giphy/MainActivity.java
+++ b/samples/giphy/src/main/java/com/bumptech/glide/samples/giphy/MainActivity.java
@@ -20,7 +20,6 @@ import com.bumptech.glide.ListPreloader;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.util.ViewPreloadSizeProvider;
-import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
@@ -31,15 +30,12 @@ public class MainActivity extends Activity implements Api.Monitor {
private static final String TAG = "GiphyActivity";
private GifAdapter adapter;
- private DrawableRequestBuilder gifItemRequest;
- private ViewPreloadSizeProvider preloadSizeProvider;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
- Glide.get(this).register(Api.GifResult.class, InputStream.class, new GiphyModelLoader.Factory());
ImageView giphyLogoView = (ImageView) findViewById(R.id.giphy_logo_view);
Glide.with(this)
@@ -49,12 +45,12 @@ public class MainActivity extends Activity implements Api.Monitor {
ListView gifList = (ListView) findViewById(R.id.gif_list);
- gifItemRequest = Glide.with(this)
+ DrawableRequestBuilder gifItemRequest = Glide.with(this)
.from(Api.GifResult.class)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.fitCenter();
- preloadSizeProvider = new ViewPreloadSizeProvider();
+ ViewPreloadSizeProvider preloadSizeProvider = new ViewPreloadSizeProvider();
adapter = new GifAdapter(this, gifItemRequest, preloadSizeProvider);
gifList.setAdapter(adapter);
ListPreloader preloader = new ListPreloader(adapter, preloadSizeProvider, 2);
--
GitLab