Can I just inject super class when use dagger2 for dependency injection?

后端 未结 3 938
你的背包
你的背包 2020-12-04 16:57

I use Dagger2 for DI in my android application. I found that I have to write inject method for every class that uses @Inject field. Is there a way that I can just inject the

相关标签:
3条回答
  • 2020-12-04 16:58

    You can do a little hack using reflection:

    public class UiInjector {
    
        private static final String METHOD_NAME = "inject";
    
        private final UIComponent component;
    
        public UiInjector(final UIComponent component) {
            this.component = component;
        }
    
        public void inject(final Object subject) {
            try {
                component.getClass()
                        .getMethod(METHOD_NAME, subject.getClass())
                        .invoke(component, subject);
            } catch (final NoSuchMethodException exception) {
                throwNoInjectMethodForType(component, subject.getClass());
            } catch (final Exception exception) {
                throwUnknownInjectionError(exception);
            }
        }
    
        private void throwNoInjectMethodForType(final Object component, final Class subjectType) {
            throw new RuntimeException(component.getClass().getSimpleName() +
                    " doesn't have inject method with parameter type : " + subjectType);
        }
    
        private void throwUnknownInjectionError(final Exception cause) {
            throw new RuntimeException("Unknown injection error", cause);
        }
    }

    In this case, you still need to write inject method in a component, but you don't need 'inject' method in each activity, fragment, view, whatever.

    Why it's work? when we use getClass() on injection subject will get a descendant class, not base.

    Caution! In case you use Proguard, you need to add next -keep class <ComponentClass> { *; } to your rules in order to keep inject methods as is in component

    0 讨论(0)
  • 2020-12-04 17:10

    I encountered the same situation. One way to ease a bit the injection from a common component in all Activities is the following:

    1) Extend the Application class to be able to create the common component and keep a reference to it.

    public class ApplicationDagger extends Application {
    
        private ApplicationComponent component;
    
        @Override
        public void onCreate(){
            super.onCreate();
            component = DaggerApplicationComponent.builder().applicationModule(new ApplicationModule(this)).build();
        }
    
        public ApplicationComponent getComponent(){
                return component;
        }
    }
    

    2) Create an abstract DaggerActivity which gets the common component from Application and calls an abstract method injectActivity, giving the component as an argument. Like this:

    public abstract class DaggerActivity extends Activity {
    
        @Override
        public void onCreate(Bundle saved){
            super.onCreate(saved);
            ApplicationComponent component = ((ApplicationDagger) getApplication()).getComponent();
            injectActivity(component);
        }
    
        public abstract void injectActivity(ApplicationComponent component);
    }
    

    3) Last, you have to actually inject each Activity extending DaggerActivity. But this can be done with less efforts now, as you have to implement the abstract method otherwise you'll get compile errors. Here we go:

    public class FirstActivity extends DaggerActivity {
    
        @Inject
        ClassToInject object;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //initialize your Activity
        }
    
        @Override
        public void injectActivity(ApplicationComponent component) {
            component.inject(this);
        }
    }
    

    Of course, you still have to declare each Activity explicitly in your Component.

    UPDATE : Injecting @ActivityScope objects into Fragments

    At some point, I needed to use custom scopes to bind objects to an Activity life cycle. I decided to extends this post as it might help some people.

    Let's say you have a @Module class ActivityModule and a @Subcomponent interface ActivityComponent.

    You would need to modify the DaggerActivity. The Activities extending DaggerActivity would need to implement the new method (change of signature).

    public abstract class ActivityDagger extends AppCompatActivity {
    
        ActivityComponent component;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            component = ((ApplicationDagger) getApplication()).getComponent().plus(new ActivityModule(this));
            injectActivity(component);
            super.onCreate(savedInstanceState);
        }
    
        ActivityComponent getComponent() {
            return component;
        }
    
        public abstract void injectActivity(ActivityComponent component);
    }
    

    Then, a class FragmentDagger extending Fragment can be created like this :

    public abstract class FragmentDagger extends Fragment {
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ActivityDagger activityDagger = (ActivityDagger) getActivity();
            ActivityComponent component = activityDagger.getComponent();
            injectFragment(component);
        }
    
        public abstract void injectFragment(ActivityComponent component);
    
    }
    

    As for the Activities, the Fragments extending FragmentDagger have only one method to implement:

    public abstract void injectFragment(ActivityComponent component);
    

    You should be able to reuse Fragments wherever you want. Notice that the method super.onCreated() in ActivityDagger should be called after the component instantiation. Otherwise, you will get NullPointerException when the Activity state is recreated, because the method super.onCreate() of the Fragment will be called.

    0 讨论(0)
  • 2020-12-04 17:11

    It cannot be done right now. Explanation by Gregory Kick (from here):

    Here's how members injection methods work:

    1. You can make a members injection method for any type that has @Inject anywhere in its class hierarchy. If it doesn't, you'll get an error.
    2. All @Injected members in the entire type hierarchy will be injected: the argument type and all supertypes.
    3. No members will be @Injected for subtypes of the argument type.

    This issue was discussed here and here, follow up these for updates. But it is unlikely to change soon, cause Dagger 2 is close to release.

    0 讨论(0)
提交回复
热议问题