Skip to content

Instantly share code, notes, and snippets.

@konmik
Last active September 8, 2020 21:36
Show Gist options
  • Save konmik/6ac725fa7134402539c4 to your computer and use it in GitHub Desktop.
Save konmik/6ac725fa7134402539c4 to your computer and use it in GitHub Desktop.
Dagger 2 injection with inheritance
/**
* This class allows to inject into objects through a base class,
* so we don't have to repeat injection code everywhere.
*
* The performance drawback is about 0.013 ms per injection on a very slow device,
* which is negligible in most cases.
*
* Example:
* <pre>{@code
* Component {
* void inject(B b);
* }
*
* class A {
* void onCreate() {
* componentReflectionInjector.inject(this);
* }
* }
*
* class B extends A {
* @Inject MyDependency dependency;
* }
*
* new B().onCreate() // dependency will be injected at this point
*
* class C extends B {
*
* }
*
* new C().onCreate() // dependency will be injected at this point as well
* }</pre>
*
* @param <T> a type of dagger 2 component.
*/
public final class ComponentReflectionInjector<T> implements Injector {
private final Class<T> componentClass;
private final T component;
private final HashMap<Class<?>, Method> methods;
public ComponentReflectionInjector(Class<T> componentClass, T component) {
this.componentClass = componentClass;
this.component = component;
this.methods = getMethods(componentClass);
}
public T getComponent() {
return component;
}
@Override
public void inject(Object target) {
Class targetClass = target.getClass();
Method method = methods.get(targetClass);
while (method == null && targetClass != null) {
targetClass = targetClass.getSuperclass();
method = methods.get(targetClass);
}
if (method == null)
throw new RuntimeException(String.format("No %s injecting method exists in %s component", target.getClass(), componentClass));
try {
method.invoke(component, target);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static final ConcurrentHashMap<Class<?>, HashMap<Class<?>, Method>> cache = new ConcurrentHashMap<>();
private static HashMap<Class<?>, Method> getMethods(Class componentClass) {
HashMap<Class<?>, Method> methods = cache.get(componentClass);
if (methods == null) {
synchronized (cache) {
methods = cache.get(componentClass);
if (methods == null) {
methods = new HashMap<>();
for (Method method : componentClass.getMethods()) {
Class<?>[] params = method.getParameterTypes();
if (params.length == 1)
methods.put(params[0], method);
}
cache.put(componentClass, methods);
}
}
}
return methods;
}
}
@konmik
Copy link
Author

konmik commented Aug 17, 2015

This is a proguard-friendly solution.

@IlyaEremin
Copy link

You are right, sorry

@bejibx
Copy link

bejibx commented Apr 8, 2016

Can't get how to use it, do you have some examples?

@esafirm
Copy link

esafirm commented Apr 21, 2016

Seems not working if Proguard is enabled, any idea?

@nbsith
Copy link

nbsith commented Jun 10, 2016

I also encountered problem with Dagger 2 and don't know why. In my case it seems that inject(presenter) methods in component are not preserved as they are not called directly in code but only via reflection. I have dontoptimize setting enabled so Proguard should not strip them but it is.

@sevar83
Copy link

sevar83 commented Jul 13, 2016

Does it have any ProGuard requirements? Seems elegant solution but I'm afraid of ProGuard.

@sgc-code
Copy link

You need to tell proguard to keep the Dagger2 Components:

-keep @interface dagger.Component
-keepclassmembers @dagger.Component class * { *; }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment