Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dagger 2 Adding a subcomponent to a parent component

Hi community I have problem with understanding dagger 2 adding subcomponent in the new way (added in dagger 2.7). See example below:

@Component(modules = {AppModule.class, MainActivityBinder.class})
@Singleton
interface AppComponent
{
   inject(MyApplication _)
}

@Subcomponent(modules = ActivityModule.class)
interface ActivitySubcomponent
{
   inject(MainActivity _)

   @Subcomponent.Builder
   interface Builder
   {
      @BindInstance
      Builder activity(Activity activity)

      ActivitySubcomponent build();
   }
}

Initial step: I have AppComponent with is my root component, that provide AppModule with singletons (retrofit, okhttp etc.) In ActivitySubcomponent I provide ActivityModule with has dependencies specified to that activity. Now subcomponent must be added to AppComponent, so in new way I create specified module called MainActivityBinder, that has annotations @Module.subcomponents with point to that binds subcomponent, but I have first problem, what should be in body of that bind module ?

@Module(subcomponents = ActivitySubcomponent.class)
public class MainActivityBinder
{
  //what body of this class should be ??
}

I know, that idea is that I can bind subcomponent or their builder. Second question when bind builder, and when bind subcomponent ? For example my ActivitySubcomponent required activity context, so I create builder that provide context to ActivityModule in this case will be better to provide in MainActivityBinder a builder ? Third question how invoke component builder and how obtain subcomponent for app component ? In standard subcomponent factory I added to AppComponent method that return subcomponent and I can define params (for example give activity context, listed below)

@Component(modules = {AppModule.class})
@Singleton
interface AppComponent
{
   ActivitySubcomponents newActivitySubcomponents(Activity activity);

   inject(MyApplication _);
}

// in MainActivity
appComponent.newActivitySubcomponents(this).build().inject(this);

so have achieve this behavior in new subcomponent added method ?

like image 746
Heroes84 Avatar asked Nov 07 '22 05:11

Heroes84


1 Answers

  1. Your module MainActivityBinder is allowed to be empty, and should be if you don't have anything else to bind with it. Empty (annotation-only) modules are also useful when you only use Module.includes, such as when you want to keep a module list in one place rather than duplicating it among several components. The subcomponents attribute on the annotation is enough for Dagger to understand what you're trying to do.

  2. You can inject a FooSubcomponent or Provider if and only if it has no @BindsInstance methods or instantiable modules (that Dagger can't instantiate). If all of your modules are interfaces, abstract classes, or modules that have public zero-arg constructors, then you may inject the subcomponent directly. Otherwise you should inject your subcomponent builder instead.

  3. You can get to your subcomponent builder by creating a method that returns it on your AppComponent, just as you can for any binding that exists in the graph:

    @Component(modules = {AppModule.class, MainActivityBinder.class})
    @Singleton
    interface AppComponent {
      ActivitySubcomponent.Builder activitySubcomponentBuilder();
    
      inject(MyApplication _)
    }
    

    You may also inject it into the object of your choice.

    @Inject ActivitySubcomponent.Builder activitySubComponentBuilder;
    activitySubComponentBuilder.activity(this).build().inject(this);
    
    // You can also inject a Provider<ActivitySubcomponent.Builder> if you want,
    // which is a good idea if you are injecting this directly into your Application.
    // Your Application will outlive your Activity, and may need to inject several
    // instances of the Activity across application lifetime.
    @Inject Provider<ActivitySubcomponent.Builder> activitySubComponentBuilderProvider;
    activitySubComponentBuilderProvider.get().activity(this).build().inject(this);
    

Though there doesn't appear to be much advantage to injecting the subcomponent builder when you could just as easily call the method on the component (either to return the Builder or return the subcomponent), there are a couple of advantages to injecting the builder:

  • Dagger can't tell whether you call a method on the component, so it generates and compiles the code even if your subcomponent is not consumed. Dagger can tell whether you ever try to inject the builder, so if there are no subcomponent/builder injections and no methods, Dagger will skip generating code for the subcomponent.
  • If your codebase is large enough that you have to split it up into different targets to compile, the factory method technique might catch you in some dependency cycles, where your app's component and modules depend on everything and you can only get to your subcomponent from your component itself. With injectable subcomponent builders, you have more options about how to get to your subcomponent or builder.
like image 127
Jeff Bowman Avatar answered Nov 09 '22 23:11

Jeff Bowman