diff --git a/app/build.gradle b/app/build.gradle index 211d2bc0ad..5c1df6df94 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,6 +48,7 @@ dependencies { final HAMCREST_VERSION = '1.3' final MOCKITO_VERSION = '1.10.19' final STORIO_VERSION = '1.4.0' + final NUCLEUS_VERSION = '2.0.1' compile fileTree(dir: 'libs', include: ['*.jar']) @@ -65,6 +66,9 @@ dependencies { compile 'io.reactivex:rxandroid:1.0.1' compile "com.pushtorefresh.storio:sqlite:$STORIO_VERSION" compile "com.pushtorefresh.storio:sqlite-annotations:$STORIO_VERSION" + compile "info.android15.nucleus:nucleus:$NUCLEUS_VERSION" + compile "info.android15.nucleus:nucleus-support-v4:$NUCLEUS_VERSION" + compile "info.android15.nucleus:nucleus-support-v7:$NUCLEUS_VERSION" compile 'de.greenrobot:eventbus:2.4.0' compile 'com.github.bumptech.glide:glide:3.6.1' compile 'com.jakewharton:butterknife:7.0.1' diff --git a/app/src/main/java/eu/kanade/mangafeed/App.java b/app/src/main/java/eu/kanade/mangafeed/App.java index e23faa4378..4d3ee28769 100644 --- a/app/src/main/java/eu/kanade/mangafeed/App.java +++ b/app/src/main/java/eu/kanade/mangafeed/App.java @@ -17,6 +17,7 @@ import timber.log.Timber; public class App extends Application { AppComponent mApplicationComponent; + ComponentReflectionInjector mComponentInjector; @Override public void onCreate() { @@ -27,6 +28,9 @@ public class App extends Application { .appModule(new AppModule(this)) .build(); + mComponentInjector = + new ComponentReflectionInjector<>(AppComponent.class, mApplicationComponent); + //ACRA.init(this); } @@ -38,6 +42,14 @@ public class App extends Application { return mApplicationComponent; } + public ComponentReflectionInjector getComponentReflection() { + return mComponentInjector; + } + + public static ComponentReflectionInjector getComponentReflection(Context context) { + return get(context).getComponentReflection(); + } + public static AppComponent getComponent(Context context) { return get(context).getComponent(); } diff --git a/app/src/main/java/eu/kanade/mangafeed/ComponentReflectionInjector.java b/app/src/main/java/eu/kanade/mangafeed/ComponentReflectionInjector.java new file mode 100644 index 0000000000..99c97a46c3 --- /dev/null +++ b/app/src/main/java/eu/kanade/mangafeed/ComponentReflectionInjector.java @@ -0,0 +1,97 @@ +package eu.kanade.mangafeed; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 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: + *
{@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
+ * }
+ * + * @param a type of dagger 2 component. + */ +public final class ComponentReflectionInjector { + + private final Class componentClass; + private final T component; + private final HashMap, Method> methods; + + public ComponentReflectionInjector(Class componentClass, T component) { + this.componentClass = componentClass; + this.component = component; + this.methods = getMethods(componentClass); + } + + public T getComponent() { + return component; + } + + 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, HashMap, Method>> cache = new ConcurrentHashMap<>(); + + private static HashMap, Method> getMethods(Class componentClass) { + HashMap, 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; + } +} \ No newline at end of file