How do I use AndroidInjection class in custom views or other android classes?

前端 未结 4 1707
日久生厌
日久生厌 2020-12-14 02:27

My issue with the Android-specific pattern is, if you use their AndroidInjection class, there is no way to members inject other objects besides Activities

4条回答
  •  臣服心动
    2020-12-14 02:54

    First, you should think over Vasily's answer.

    But let's think for a moment how we did this before Dagger Android? We built a subcomponent from the component that was taken from the Application class. Later, we could use this subcomponent in order to inject fields, for example, of a custom view.

    So, we'll try to do the exact same thing now.

    Suppose, our aim is to inject MyAdapter class into a MyButton:

    public class MyButton extends AppCompatButton {
    
        @Inject MyAdapter adapter;
    
        public MyButton(Context context) {
            super(context);
    
            ...
        }
    
    }
    

    And let's make the adapter have a dependency on the activity Context, not application Context:

    public class MyAdapter {
    
        @Inject
        public MyAdapter(@Named("activity") Context context) {
        }
    
    }
    

    Let's start with the custom Application class.

    MyApplication.java

    public class MyApplication extends DaggerApplication {
    
        @Inject
        DispatchingAndroidInjector dispatchingActivityInjector;
    
        public static MySubcomponent mySubcomponent;
    
        @Override
        protected AndroidInjector applicationInjector() {
    
            return DaggerAppComponent.builder()
                    .create(this);
        }
    
    }
    

    AppComponent.java:

    @Component(modules = {AndroidSupportInjectionModule.class, ActivityBindingModule.class, AppModule.class})
    @Singleton
    public interface AppComponent extends AndroidInjector {
    
        @Component.Builder
        abstract class Builder extends AndroidInjector.Builder {
        }
    }
    

    AppModule.java

    @Module
    abstract class AppModule {
    
        @Binds
        @Singleton
        @Named("app")
        abstract Context providesContext(Application application);
    }
    

    ActivityBindingModule.java

    @Module(subcomponents = MySubcomponent.class)
    public abstract class ActivityBindingModule {
    
        @Binds
        @IntoMap
        @ActivityKey(MainActivity.class)
        abstract AndroidInjector.Factory
        bindMainActivityInjectorFactory(MySubcomponent.Builder builder);
    }
    

    AndroidSupportInjectionModule.java is shipping with dagger itself. If you do not use classes from support package (i.e. android.support.v4.app.Fragment instead of android.app.Fragment), then use AndroidInjectionModule.java.

    MySubcomponent.java

    @ActivityScope
    @Subcomponent(modules = {SubcomponentModule.class/*, other modules here, if needed */})
    public interface MySubcomponent extends AndroidInjector {
    
        void inject(MyButton button);
    
        @Subcomponent.Builder
        abstract class Builder extends AndroidInjector.Builder {
            public abstract MySubcomponent build();
        }
    }
    

    SubcomponentModule.java

    @Module
    abstract class SubcomponentModule {
    
        @Binds
        @ActivityScope
        @Named("activity")
        abstract Context toContext(MainActivity activity);
    
    }
    

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
    
        @Inject
        MySubcomponent subcomponent;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // Will inject `subcomponent` field
            AndroidInjection.inject(this);
    
            // Saving this component in a static field
            // Hereafter you are taking responsibility of mySubcomponent lifetime
            MyApplication.mySubcomponent = subcomponent;
    
            super.onCreate(savedInstanceState);
            setContentView(new MyButton(this));
        }
    }
    

    Having all of these, now here's how MyButton will look like:

    public class MyButton extends AppCompatButton {
    
        @Inject MyAdapter adapter;
    
        public MyButton(Context context) {
            super(context);
    
            MyApplication.mySubcomponent.inject(this);
        }
    
    }
    

    I admit that this looks hacky and certainly not an approach to stick to. I'm happy to see a better approach.

提交回复
热议问题