Last active
September 8, 2020 21:36
-
-
Save konmik/6ac725fa7134402539c4 to your computer and use it in GitHub Desktop.
Dagger 2 injection with inheritance
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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; | |
} | |
} |
This is a proguard-friendly solution.
You are right, sorry
Can't get how to use it, do you have some examples?
Seems not working if Proguard is enabled, any idea?
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.
Does it have any ProGuard requirements? Seems elegant solution but I'm afraid of ProGuard.
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
And you will lost most valuable benefit of dagger 2 - proguard without headache.