Skip to content

Commit

Permalink
Add GlideModule for lazily initialization.
Browse files Browse the repository at this point in the history
  • Loading branch information
sjudd committed Jan 22, 2015
1 parent e099ef6 commit e6a4ea3
Show file tree
Hide file tree
Showing 14 changed files with 381 additions and 11 deletions.
7 changes: 5 additions & 2 deletions integration/okhttp/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bumptech.glide.integration.okhttp">
<application />

<application>
<meta-data
android:name="com.bumptech.glide.integration.okhttp.OkHttpGlideModule"
android:value="GlideModule" />
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -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}.
*
* <p>
* 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}.
* </p>
*/
public class OkHttpGlideModule implements GlideModule {
@Override
public void initialize(Context context, Glide glide) {
glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
}
}
6 changes: 5 additions & 1 deletion integration/volley/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bumptech.glide.integration.volley">
<application />
<application>
<meta-data
android:name="com.bumptech.glide.integration.volley.VolleyGlideModule"
android:value="GlideModule" />
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -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}.
*
* <p>
* 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}.
* </p>
*/
public class VolleyGlideModule implements GlideModule {
@Override
public void initialize(Context context, Glide glide) {
glide.register(GlideUrl.class, InputStream.class, new VolleyUrlLoader.Factory(context));
}
}
Original file line number Diff line number Diff line change
@@ -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<GlideModule> 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<GlideModule> 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();
}
}
}
6 changes: 6 additions & 0 deletions library/src/main/java/com/bumptech/glide/Glide.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
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;
Expand Down Expand Up @@ -241,6 +243,10 @@ static void tearDown() {

bitmapFitCenter = new FitCenter(bitmapPool);
drawableFitCenter = new GifBitmapWrapperTransformation(bitmapPool, bitmapFitCenter);

for (GlideModule module : new ManifestParser(context).parse()) {
module.initialize(context, this);
}
}

/**
Expand Down
57 changes: 57 additions & 0 deletions library/src/main/java/com/bumptech/glide/module/GlideModule.java
Original file line number Diff line number Diff line change
@@ -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}.
*
* <p>
* To use this interface:
* <ol>
* <li>
* 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:
* <pre>
* <code>
* public class FlickrGlideModule implements GlideModule {
* {@literal @}Override
* public void initialize(Context context, Glide glide) {
* glide.register(Model.class, Data.class, new MyModelLoader());
* }
* }
* </code>
* </pre>
* </li>
* <li>
* Add your implementation to your list of keeps in your proguard.cfg file:
* <pre>
* {@code
* -keepnames class * com.bumptech.glide.samples.flickr.FlickrGlideModule
* }
* </pre>
* </li>
* <li>
* 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:
* <pre>
* {@code
* <meta-data
* android:name="com.bumptech.glide.samples.flickr.FlickrGlideModule"
* android:value="GlideModule" />
* }
* </pre>
* </li>
* </ol>
* </p>
*
* <p>
* All implementations must be publicly visible and contain only an empty constructor so they can be instantiated
* via reflection when Glide is lazily initialized.
* </p>
*/
public interface GlideModule {
void initialize(Context context, Glide glide);
}
Original file line number Diff line number Diff line change
@@ -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<GlideModule> parse() {
List<GlideModule> modules = new ArrayList<GlideModule>();
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;
}
}
3 changes: 3 additions & 0 deletions samples/flickr/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@

<activity android:name=".FullscreenActivity" />

<meta-data
android:name="com.bumptech.glide.samples.flickr.FlickrGlideModule"
android:value="GlideModule" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -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());
}
}
Loading

0 comments on commit e6a4ea3

Please sign in to comment.