Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to selectively set modules for components in Dagger 2?

Caused by: java.lang.IllegalStateException: analyticsModule must be set

I'm building a library that uses template-style initialization. The user can selectively setup modules for the project with that library. It uses Dagger 2 for DI.

But Dagger 2 doesn't seem to allow optional modules. Can't unset modules be simply ignored?

like image 251
razzledazzle Avatar asked Dec 04 '15 10:12

razzledazzle


People also ask

What is component and module in Dagger 2?

For a fast read, a Component in Dagger is an unit which is essentially used to resolve dependencies. A Module is a sub-unit, providing dependencies, one or several of which can be used by a Component. A SubComponent is a type of Component which derives from a Component, inheriting the dependencies it already provides.

Can we create custom scope in Dagger 2?

create custom scopes with Dagger is pretty easy, you just have to follow these steps. Step 1) declare your annotation. Step 2) annotate the dependencies with this annotation in the module.

Is Dagger 2 deprecated?

It's officially deprecated and you can pretty much ignore it. Google's framework, which became dominant in Android ecosystem, was originally called Dagger 2. Sometimes we still refer to it as such, but, in most cases, we simply call it Dagger today.

What are the components in Dagger 2?

Components are essentially the glue that holds everything together. They are a way of telling Dagger 2 what dependencies should be bundled together and made available to a given instance so they can be used. They provide a way for a class to request dependencies being injected through their @Inject annotation.


1 Answers

You might want to consider using Multibindings, which allows for users to optionally add in dependencies into a Set<T> or Map<K,V>. Here's an example:

interface Plugin {
    void install(Application application);
}

@Component({ModuleA.class, ModuleB.class})
interface PluginComponent {
    Set<Plugin> plugins();
}

@Module
class ModuleA {
    @Provides(type = SET) Plugin providFooPlugin() {
        return new FooPlugin();
    }
}

@Module
class ModuleB {
    @Provides(type = SET) Plugin providBarPlugin() {
        return new BarPlugin();
    }
}

In this case, you still need an instance of each module, even if it's not used. One option to get around this would be to use @Provides(type = SET_VALUES), and have modules that you wan't turned off to return Collections.emptySet(). Here's a modified example:

interface Plugin {
    void install(Application application);
}

@Component({ModuleA.class, ModuleB.class})
interface PluginComponent {
    Set<Plugin> plugins();
}

@Module
class ModuleA {
    private final Set<Plugin> plugins;

    ModuleA(Set<Plugin> plugins) {
        this.plugins = plugins;
    }

    @Provides(type = SET_VALUES) Plugin providFooPlugins() {
        return plugins;
    }
}

@Module
class ModuleB {
    @Provides(type = SET) Plugin providBarPlugin() {
        return new BarPlugin();
    }
}

Now, you can call:

DaggerPluginComponent.builder()
    .moduleA(new ModuleA(Collections.emptySet())
    .build();

Or alternatively:

Set<Plugin> plugins = new HashSet<>();
plugins.add(new AwesomePlugin());
plugins.add(new BoringPlugin());
DaggerPluginComponent.builder()
    .moduleA(new ModuleA(plugins)
    .build();
like image 155
rdshapiro Avatar answered Sep 28 '22 07:09

rdshapiro