Dagger 2.10 Android Subcomponents and Builders

Dagger 2.10 Android subcomponents and builders

In short, you're supposed to override the call to seedInstance on the Builder (which is an abstract class instead of an interface) to provide other modules you need.

edit: Before you do, check and make sure that you really need to pass that Module. As Damon added in a separate answer, if you're making a specific Module for your Android class, you can rely on the automatic injection of that class to pull the configuration or instance out of the graph at that point. Favor his approach if it's easier just to eliminate the constructor parameters from your Module, which also may provide better performance as they avoid unnecessary instances and virtual method calls.


First, dagger.android in 30 seconds: Rather than having each Activity or Fragment know about its parent, the Activity (or Fragment) calls AndroidInjection.inject(this), which checks the Application for HasActivityInjector (or parent fragments, activity, and application for HasFragmentInjector). The idea is that you contribute a binding to a multibindings-created Map<Class, AndroidInjector.Factory>, where the contributed bindings are almost always subcomponent builders you write that build object-specific subcomponents.

As you might tell from AndroidInjection.inject(this) and AndroidInjector.Factory.create(T instance), you don't get a lot of opportunity to pass Activity-specific or Fragment-specific details to your Builder. Instead, the idea is that your subcomponent builder overrides the seedInstance implementation. As in the docs for seedInstance:

Provides instance to be used in the binding graph of the built AndroidInjector. By default, this is used as a BindsInstance method, but it may be overridden to provide any modules which need a reference to the activity.

This should be the same instance that will be passed to inject(Object).

That'd look something like this:

@Subcomponent(modules = {OneModule.class, TwoModule.class})
public interface YourActivitySubcomponent extends AndroidInjector<YourActivity> {

// inject(YourActivity) is inherited from AndroidInjector<YourActivity>

@Builder
public abstract class Builder extends AndroidInjector.Builder<YourActivity> {
// Here are your required module builders:
abstract Builder oneModule(OneModule module);
abstract Builder twoModule(TwoModule module);

// By overriding seedInstance, you don't let Dagger provide its
// normal @BindsInstance implementation, but you can supply the
// instance to modules or call your own BindsInstance:
@Override public void seedInstance(YourActivity activity) {
oneModule(new OneModule(activity));
twoModule(new TwoModule(activity.getTwoModuleParameter()));
}
}
}

The assumption here is that you need to wait for the activity instance for the modules. If not, then you also have the option of calling those when you bind the subcomponent:

@Provides @IntoMap @ActivityKey(YourActivity.class)
AndroidInjector.Factory bindInjector(YourActivitySubcomponent.Builder builder) {
return builder
.oneModule(new OneModule(...))
.twoModule(new TwoModule(...));
}

...but if you can do that, then you could more-easily take care of those bindings by overriding those modules, implementing a zero-arg constructor that can supply the Module's constructor parameters, and letting Dagger create those as it does for any Modules with public zero-arg constructors.

Dagger 2 @Component.Builder dependencies missing setters error

Since you have dependencies = [LibraryClient.Dependency::class] as @Component dependency you have to add a corresponding @Component.Builder method (setter) too.

interface Builder {
...
fun libraryClient(libraryClient: LibraryClient.Dependency): Builder
...
}

Dagger 2.10 subcomponent generator - Injector validation fails

If anyone is interested in solution:

I found out that for some reason, references of ClassType-s compared during project's compile time are not the same when validating generated method.

And these references, despite the fact that they are pointing to the same class, are checked for equality in auto-common library in EqualVisitor.visitDeclared method. Apparently, this can be a bug in auto-common, because Elements in visitDeclared are compared by object reference, but not type reference.

So workaround here is to use local fixed copy of auto-common library and exclude all dependencies of the original library.

//TODO think if this is the correct solution to cast both elements
//return aElement.equals(bElement)
return ((TypeElement) aElement).getQualifiedName().equals(((TypeElement) bElement).getQualifiedName())
&& equal(a.getEnclosingType(), b.getEnclosingType(), newVisiting)
&& equalLists(a.getTypeArguments(), b.getTypeArguments(), newVisiting);



I still have to check why those references are not the same, and I have to think how the equality check can be fixed properly in auto-common (I use just a quickfix) before filing an issue in auto-common repo.

Dagger-Android @ActivityKey not found, how to create Sub components explicitly

After inspecting the generated code i found that it's now used as @ClassKey in AppModule

@ClassKey(MainActivity::class)
abstract fun bindInjectorFactory(builder: MainActivitySubComponent.Builder)
: AndroidInjector.Factory<*>

instead of

  @Binds
@IntoMap
@ActivityKey(MainActivity::class)
abstract fun bindInjectorFactory(builder: MainActivitySubComponent.Builder)
: AndroidInjector.Factory<out Activity>

and ClassKey code is

@Documented
@Target(METHOD)
@Retention(RUNTIME)
@MapKey
public @interface ClassKey {
Class<?> value();
}

Dagger - Setting a Dynamic Value to SubComponent Builder

dagger.android works by automatically creating your component in onCreate, and needs to work this way because Android is allowed to create your Activity on its own. Pre-creating your AndroidInjector will not work; that is what's wrong with your previous approach.

As I tried to indicate through a duplicate-question vote, the only way for you to set module instance or @BindsInstance values in dagger.android is by overriding the seedInstance method in the AndroidInjector.Builder that serves as your @Subcomponent.Builder as well. This will let you pull values out of the Activity instance and set them in your Activity graph.

Though you could always stash data in a @Singleton (ApplicationComponent-scoped) object or VM global variable, you are trying to do the right thing by passing data carefully from Activity to Activity without setting global state. I think this is the right approach, but also that your solution probably will not involve constructing an AndroidInjector explicitly. Instead, pass your data through the extras bundle, which is an idiomatic way of transferring parameters and other data into Activities; you can then insert that data into your graph by pulling it out of getIntent().getExtras() on your Activity instance in seedInstance, since getIntent() should be populated by the time onCreate(Bundle) runs.



Related Topics



Leave a reply



Submit