
roboguice.RoboGuice Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of roboguice Show documentation
Show all versions of roboguice Show documentation
A framework for using Google Guice dependency injection in Android.
package roboguice;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.WeakHashMap;
import roboguice.config.DefaultRoboModule;
import roboguice.config.RoboGuiceHierarchyTraversalFilter;
import roboguice.event.EventManager;
import roboguice.inject.ContextScope;
import roboguice.inject.ContextScopedRoboInjector;
import roboguice.inject.ResourceListener;
import roboguice.inject.RoboInjector;
import roboguice.inject.ViewListener;
import roboguice.util.Strings;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Stage;
import com.google.inject.HierarchyTraversalFilter;
import com.google.inject.HierarchyTraversalFilterFactory;
import com.google.inject.Module;
import com.google.inject.internal.util.Stopwatch;
import com.google.inject.util.Modules;
import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
/**
*
* Manages injectors for RoboGuice applications.
*
* There are two types of injectors:
*
* 1. The base application injector, which is not typically used directly by the user.
* 2. The ContextScopedInjector, which is obtained by calling {@link #getInjector(android.content.Context)}, and knows about
* your current context, whether it's an activity, service, or something else.
*
* BUG hashmap should also key off of stage and modules list
* TODO add documentation about annotation processing system
*/
public final class RoboGuice {
@edu.umd.cs.findbugs.annotations.SuppressWarnings("MS_SHOULD_BE_FINAL")
@SuppressWarnings({"checkstyle:visibilitymodifier","checkstyle:staticvariablename"})
public static Stage DEFAULT_STAGE = Stage.PRODUCTION;
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="MS_SHOULD_BE_FINAL")
protected static WeakHashMap injectors = new WeakHashMap();
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="MS_SHOULD_BE_FINAL")
protected static WeakHashMap resourceListeners = new WeakHashMap();
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="MS_SHOULD_BE_FINAL")
protected static WeakHashMap viewListeners = new WeakHashMap();
/** Enables or disables using annotation databases to optimize roboguice. Used for testing. Enabled by default.*/
private static boolean useAnnotationDatabases = true;
//used for testing
static {
String useAnnotationsEnvVar = System.getenv("roboguice.useAnnotationDatabases");
if( useAnnotationsEnvVar != null ) {
useAnnotationDatabases = Boolean.parseBoolean(useAnnotationsEnvVar);
}
}
private RoboGuice() {
}
/**
* Return the cached Injector instance for this application, or create a new one if necessary.
*/
public static Injector getOrCreateBaseApplicationInjector(Application application) {
Injector rtrn = injectors.get(application);
if( rtrn!=null )
return rtrn;
synchronized (RoboGuice.class) {
rtrn = injectors.get(application);
if( rtrn!=null )
return rtrn;
return getOrCreateBaseApplicationInjector(application, DEFAULT_STAGE);
}
}
/**
* Return the cached Injector instance for this application, or create a new one if necessary.
* If specifying your own modules, you must include a DefaultRoboModule for most things to work properly.
* Do something like the following:
*
* RoboGuice.setApplicationInjector( app, RoboGuice.DEFAULT_STAGE, Modules.override(RoboGuice.newDefaultRoboModule(app)).with(new MyModule() );
*
* @see com.google.inject.util.Modules#override(com.google.inject.Module...)
* @see roboguice.RoboGuice#createBaseApplicationInjector(android.app.Application, com.google.inject.Stage, String[], com.google.inject.Module...)
* @see roboguice.RoboGuice#newDefaultRoboModule(android.app.Application)
* @see roboguice.RoboGuice#DEFAULT_STAGE
*
* If using this method with test cases, be sure to call {@link roboguice.RoboGuice.Util#reset()} in your test teardown methods
* to avoid polluting our other tests with your custom injector. Don't do this in your real application though.
* One of RoboGuice's entry points.
*/
public static Injector getOrCreateBaseApplicationInjector(final Application application, Stage stage, Module... modules ) {
final Stopwatch stopwatch = new Stopwatch();
synchronized (RoboGuice.class) {
initializeAnnotationDatabaseFinderAndHierarchyTraversalFilterFactory(application);
return createGuiceInjector(application, stage, stopwatch, modules);
}
}
/**
* Shortcut to obtain an injector during tests. It will load all modules declared in manifest, add a {@code DefaultRoboModule},
* and override all bindings defined in test modules. We use default stage by default.
*
* RoboGuice.overrideApplicationInjector( app, new TestModule() );
*
* @see com.google.inject.util.Modules#override(com.google.inject.Module...)
* @see roboguice.RoboGuice#getOrCreateBaseApplicationInjector(android.app.Application, com.google.inject.Stage, com.google.inject.Module...)
* @see roboguice.RoboGuice#newDefaultRoboModule(android.app.Application)
* @see roboguice.RoboGuice#DEFAULT_STAGE
*
* If using this method with test cases, be sure to call {@link roboguice.RoboGuice.Util#reset()} in your test teardown methods
* to avoid polluting our other tests with your custom injector. Don't do this in your real application though.
*/
public static Injector overrideApplicationInjector(final Application application, Module... overrideModules) {
synchronized (RoboGuice.class) {
final List baseModules = extractModulesFromManifest(application);
final Injector rtrn = Guice.createInjector(DEFAULT_STAGE, Modules.override(baseModules).with(overrideModules));
injectors.put(application,rtrn);
return rtrn;
}
}
/**
* Return the cached Injector instance for this application, or create a new one if necessary.
* One of RoboGuice's entry points.
*/
public static Injector getOrCreateBaseApplicationInjector(Application application, Stage stage) {
final Stopwatch stopwatch = new Stopwatch();
synchronized (RoboGuice.class) {
initializeAnnotationDatabaseFinderAndHierarchyTraversalFilterFactory(application);
final List modules = extractModulesFromManifest(application);
return createGuiceInjector(application, stage, stopwatch, modules.toArray(new Module[modules.size()]));
}
}
private static List extractModulesFromManifest(Application application) {
try {
final ArrayList modules = new ArrayList();
final ApplicationInfo ai = application.getPackageManager().getApplicationInfo(application.getPackageName(), PackageManager.GET_META_DATA);
final Bundle bundle = ai.metaData;
final String roboguiceModules = bundle!=null ? bundle.getString("roboguice.modules") : null;
final DefaultRoboModule defaultRoboModule = newDefaultRoboModule(application);
final String[] moduleNames = roboguiceModules!=null ? roboguiceModules.split("[\\s,]") : new String[]{};
modules.add(defaultRoboModule);
for (String name : moduleNames) {
if( Strings.notEmpty(name)) {
final Class extends Module> clazz = Class.forName(name).asSubclass(Module.class);
try {
modules.add(clazz.getDeclaredConstructor(Application.class).newInstance(application));
} catch( NoSuchMethodException ignored ) {
modules.add( clazz.newInstance() );
}
}
}
return modules;
} catch (Exception e) {
throw new RuntimeException("Unable to instantiate your Module. Check your roboguice.modules metadata in your AndroidManifest.xml",e);
}
}
private static Injector createGuiceInjector(final Application application, Stage stage, final Stopwatch stopwatch, Module... modules) {
try {
synchronized (RoboGuice.class) {
final Injector rtrn = Guice.createInjector(stage, modules);
injectors.put(application,rtrn);
return rtrn;
}
} finally {
stopwatch.resetAndLog("BaseApplicationInjector creation");
}
}
public static RoboInjector getInjector(Context context) {
final Application application = (Application)context.getApplicationContext();
return new ContextScopedRoboInjector(context, getOrCreateBaseApplicationInjector(application));
}
/**
* A shortcut for RoboGuice.getInjector(context).injectMembers(o);
*/
public static T injectMembers( Context context, T t ) {
getInjector(context).injectMembers(t);
return t;
}
public static DefaultRoboModule newDefaultRoboModule(final Application application) {
return new DefaultRoboModule(application, new ContextScope(application), getViewListener(application), getResourceListener(application));
}
public static void setUseAnnotationDatabases(boolean useAnnotationDatabases) {
RoboGuice.useAnnotationDatabases = useAnnotationDatabases;
}
@SuppressWarnings("ConstantConditions")
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NP_LOAD_OF_KNOWN_NULL_VALUE", justification="Double check lock")
protected static ResourceListener getResourceListener( Application application ) {
ResourceListener resourceListener = resourceListeners.get(application);
if( resourceListener==null ) {
synchronized (RoboGuice.class) {
if( resourceListener==null ) {
resourceListener = new ResourceListener(application);
resourceListeners.put(application,resourceListener);
}
}
}
return resourceListener;
}
@SuppressWarnings("ConstantConditions")
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NP_LOAD_OF_KNOWN_NULL_VALUE", justification="Double check lock")
protected static ViewListener getViewListener( final Application application ) {
ViewListener viewListener = viewListeners.get(application);
if( viewListener==null ) {
synchronized (RoboGuice.class) {
if( viewListener==null ) {
viewListener = new ViewListener();
viewListeners.put(application,viewListener);
}
}
}
return viewListener;
}
public static void destroyInjector(Context context) {
final RoboInjector injector = getInjector(context);
injector.getInstance(EventManager.class).destroy();
//noinspection SuspiciousMethodCalls
injectors.remove(context); // it's okay, Context is an Application
}
private static void initializeAnnotationDatabaseFinderAndHierarchyTraversalFilterFactory(Application application) {
if( useAnnotationDatabases ) {
Log.d(RoboGuice.class.getName(), "Using annotation database(s).");
long start = SystemClock.currentThreadTimeMillis();
try {
Set packageNameList = new HashSet();
try {
ApplicationInfo ai = application.getPackageManager().getApplicationInfo(application.getPackageName(), PackageManager.GET_META_DATA);
final Bundle bundle = ai.metaData;
final String roboguicePackages = bundle!=null ? bundle.getString("roboguice.annotations.packages") : null;
if( roboguicePackages != null ) {
for( String packageName : roboguicePackages.split("[\\s,]") ) {
packageNameList.add(packageName);
}
}
} catch (NameNotFoundException e) {
//if no packages are found in manifest, just log
Log.d(RoboGuice.class.getName(), "Failed to read manifest properly.");
e.printStackTrace();
}
if( packageNameList.isEmpty() ) {
//add default package if none is specified
packageNameList.add("");
}
packageNameList.add("roboguice");
Log.d(RoboGuice.class.getName(), "Using annotation database(s) : " + packageNameList.toString());
final String[] packageNames = new String[packageNameList.size()];
packageNameList.toArray(packageNames);
Guice.setAnnotationDatabasePackageNames(packageNames);
long end = SystemClock.currentThreadTimeMillis();
Log.d(RoboGuice.class.getName(), "Time spent loading annotation databases : " + (end-start));
} catch( Exception ex ) {
throw new IllegalStateException("Unable to use annotation database(s)", ex);
}
} else {
Log.d(RoboGuice.class.getName(), "Using full reflection. Try using RoboGuice annotation processor for better performance.");
Guice.setHierarchyTraversalFilterFactory(new HierarchyTraversalFilterFactory() {
@Override
public HierarchyTraversalFilter createHierarchyTraversalFilter() {
return new RoboGuiceHierarchyTraversalFilter();
}
});
}
}
public static final class Util {
private Util() {}
/**
* This method is provided to reset RoboGuice in testcases.
* It should not be called in a real application.
*/
public static void reset() {
injectors.clear();
resourceListeners.clear();
viewListeners.clear();
//clear annotation database finder
//restore hierarchy filter
Guice.setAnnotationDatabasePackageNames(null);
Guice.setHierarchyTraversalFilterFactory(new HierarchyTraversalFilterFactory());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy