Ver código fonte

Introducing nucleus

inorichi 9 anos atrás
pai
commit
235ed36fbe

+ 4 - 0
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'

+ 12 - 0
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<AppComponent> 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<AppComponent> getComponentReflection() {
+        return mComponentInjector;
+    }
+
+    public static ComponentReflectionInjector<AppComponent> getComponentReflection(Context context) {
+        return get(context).getComponentReflection();
+    }
+
     public static AppComponent getComponent(Context context) {
         return get(context).getComponent();
     }

+ 97 - 0
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:
+ * <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> {
+
+    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;
+    }
+
+    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;
+    }
+}