我如何告诉 Dagger 2 要基于 X 实例化哪个实现?

How do I tell Dagger 2 which implementation to instantiate based on X?

在模块内部,如果我需要根据模块构造时已知的变量提供接口的不同实现,我可以将逻辑放在该接口类型的@Provides 方法中。像这样:

@Module
public class FooModule {

    private final State state;

    public FooModule(State state) {
        this.state = state;
    }

    @Provides
    FooInterface provideFooImplementation() {
        switch(state) {
            case STATE_1:
                return new FooImpl1();
            case STATE_2:
                return new FooImpl2();
            ...
            case STATE_10:
                return new FooImpl10();
        }
    }
}

但是,这些实现可以由 Dagger 创建。我宁愿说 "Hey, based on X I want you to instantiate this class for me"

我考虑了几个选项。

  1. 更改 provides 方法以接受所有可能的实现:

    @Provides
    FooInterface provideFooImplementation(FooImpl1 impl1, FooImpl2 imp2, ..., FooImpl10 impl10) {
        switch(state) {
            case STATE_1:
                return impl1;
            case STATE_2:
                return impl2;
            ...
            case STATE_10:
                return impl10;
        }
    }
    

这允许 Dagger 实例化它们并满足它们的所有依赖关系,但如果每个实现都相对较大或创建成本高昂,则这不是一个好主意。

  1. 更改 provides 方法以收集不同实现的所有依赖项。

    @Provides
    FooInterface provideFooImplementation(Context context, Repo repo, HttpClient httpClient, ...) {
        switch(state) {
            case STATE_1:
                return new FooImpl1(context);
            case STATE_2:
                return new FooImpl2(repo, httpClient);
            ...
            case STATE_10:
                return new FooImpl10(context, repo);
        }
    }
    

这比选项 1 稍微好一些,因为 Dagger 不必实例化每个单独的实现,但是它仍然需要实例化所有依赖项,即使它们可能不会在所有情况下都被使用。我也回到自己创建对象,即使它们可以由 Dagger 创建。

  1. 为每个实现创建一个模块并实例化适当的模块。所以像:

    @Module
    public FooImpl1Module {
    
        @Provides
        FooInterface provideFooImplementation(Context context) {
            return new FooImpl1(context);
        }
    }
    

这很好,但现在我在定义依赖于模块的组件时遇到问题。

解决这个问题的最佳方法是什么?

一个建议是使用 Lazy 包装的参数尝试选项 1。然后我最终只在一个上调用 .get() 。我会尽可能地尝试一下,然后 post 结果

你试过这样的东西吗?

public class hectic extends Iam {

    String tokenizer heccas = new string tokenizer();
}

一种可能的解决方案是使用 @Named("foo") 注释结合支持组件提供方法而不是手动注入,但这意味着您的状态将独立于模块本身,而您就是那个做出选择

@Component(modules={FooModule.class})
public interface AppComponent {
    @Named("STATE_1")
    FooInterface fooImpl1();
    @Named("STATE_2")
    FooInterface fooImpl2();
    ...
    @Named("STATE_10")
    FooInterface fooImpl10();
}

@Module
public FooImpl1Module {
    @Provides
    @Named("STATE_1")
    FooInterface provideFooImpl1(Context context) {
        return new FooImpl1(context);
    }

    @Provides
    @Named("STATE_2")
    FooInterface provideFooImpl2(Context context) {
        return new FooImpl2(context);
    }

    ...

    @Provides
    @Named("STATE_10")
    FooInterface provideFooImpl10(Context context) {
        return new FooImpl10(context);
    }
}

那你可以打电话

FooInterface fooInterface = component.fooImpl1();

而不是 Lazy<T>,用 Provider<T> 做选项 1。 Lazy<T> 只是一个 Provider<T> 在本地记忆(使用必要的双重检查锁定),但是因为你知道你只会调用一个 Provider 一次,所以你可以只注入 Provider 并跳过同步开销。

@Provides
FooInterface provideFooImplementation(
        Provider<FooImpl1> impl1,
        Provider<FooImpl2> impl2,
        ...,
        Provider<FooImpl10> impl10) {
    switch(state) {
        case STATE_1:
            return impl1.get();
        case STATE_2:
            return impl2.get();
        ...
        case STATE_10:
            return impl10.get();
    }
}

选项 2 会起作用,但您将有效地跳过 Dagger 可以轻松为您完成的依赖关系连接,并且选项 3 不会像声明的那样起作用,因为您的 @Component 注释需要您的模块列表才能编译-Dagger 代码生成工作的时间常数。

(如果您的绑定是常量或一种形式或另一种形式的零依赖性 class,则选项 3 的变体可能有效,因为这样您就可以传入任意子 class将您的模块添加到您的组件构建器中。但是,Dagger 只能分析 superclass 模块中的绑定,如果您的 @Provides 方法实现采用与您的方法不同的参数,您将遇到麻烦,因此 switch 是我能想到的最好和最清楚的选择。)