Dagger with Conductor Controllers

956 Views Asked by At

I am experimenting with the 'new' Android support in Dagger 2.
I want to set up the following architecture in Dagger:

Application => Activity => Controller (Conductor)

(Controller is basically a View that gets instantiated by the system. You can think of it like Fragments but without Dagger Injection support)

For each level I have defined a dependency: ApplicationDep, ActivityDep and ControllerDep.

  1. My Controller should be able to inject all of these dependencies.
  2. My Activity should be able to inject the ApplicationDep and the ActivityDep
  3. My Application should only be able to inject the ApplicationDep

Everything works except in my Controller.
I am unable to inject the ActivityDep.

public class MyController extends Controller {
  @Inject
  ApplicationDependency applicationDependency;
  //@Inject
  //ActivityDependency activityDependency; // can not be provided
  @Inject
  ControllerDependency controllerDependency;

  @NonNull @Override protected View onCreateView(@NonNull LayoutInflater layoutInflater, @NonNull ViewGroup viewGroup) {
    ConductorInjection.inject(this);
    return layoutInflater.inflate(R.layout.controller_main, viewGroup, false);
  }
}

Currently I have my ControllerBindingModule bound on my ApplicationComponent, however this should be bound on the Activity in order for it to be also available in the Controller.
How can I do this?

@Singleton
@Component(modules = {
        ApplicationModule.class,
        ActivityBindingModule.class,
        AndroidSupportInjectionModule.class,

        ConductorInjectionModule.class,
        ControllerBindingModule.class
})
interface AppComponent extends AndroidInjector<App> {
    @Component.Builder
    abstract class Builder extends AndroidInjector.Builder<App> {}
}

The full code can be found on Github.
Thanks.

1

There are 1 best solutions below

5
On BEST ANSWER

It sounds like controller is a subcomponent of activity component.
I took a look at your GitHub, so I change some of your code to answer.

First, for the Activity injection.
Controller is not subcomponent of Appcomponent, so it only need ActivityBindingModule.

AppComponent.java

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

For the same reason, we only need to implement HasActivityInjector in App.

App.java

public class App extends Application implements HasActivityInjector {
    @Inject protected DispatchingAndroidInjector<Activity> dispatchingActivityInjector;

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return dispatchingActivityInjector;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        DaggerAppComponent
            .builder()
            .create(this)
            .inject(this);
    }
}

Because I need to declare Controller as subcomponent of ActivityComponent, I use step 2 & step 3 in Dagger documentation about injecting activity objects

Change your ActivityBindingModule

ActivityBindingModule.java

@Module(subcomponents = {MainActivityComponent.class})
public abstract class ActivityBindingModule {

    @Binds
    @IntoMap
    @ActivityKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity>
    bindYourActivityInjectorFactory(MainActivityComponent.Builder builder);
}

Create an ActivityComponent.

MainActivityComponent.java

@Subcomponent(modules = {MainActivityModule.class})
@ActivityScope
public interface MainActivityComponent extends AndroidInjector<MainActivity>{
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
}

Next, it time to set controller as a subcomponent of activitycomponent.
Add ControllerBindingModule.class to MainActivityComponent.

@Subcomponent(modules = {MainActivityModule.class, ControllerBindingModule.class})
@ActivityScope
public interface MainActivityComponent extends AndroidInjector<MainActivity>{
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
}

And implement HasControllerInjector in MainActivity.

MainActivity.java

public class MainActivity extends AppCompatActivity implements HasControllerInjector {

    @Inject protected DispatchingAndroidInjector<Controller> dispatchingControllerInjector;

    @Override
    public DispatchingAndroidInjector<Controller> controllerInjector() {
        return dispatchingControllerInjector;
}

No need to change other files, and what you want is done.