使用Dagger注入具有不同接口的同一对象

Eri*_*wne 5 dagger-2

假设我有一个由Dagger 2创建的单例,该单例实现了接口FooBar。代码中有使用的Foo地方,还有其他使用的地方Bar,但是无论注入哪个接口,我都希望使用相同的单例。这可能吗?

Jef*_*ica 7

是的,这很容易做到,但也容易出错。诀窍是您需要小心制作@Singleton:Foo,Bar或FooBarImpl。

在Dagger中,任何特定的绑定都可以标记为Singleton,这就是Dagger干扰说“让我缓存此实例”的地方。像Foo和Bar这样的接口将在@Bindsor @Provides方法上对其进行标记,而像FooBarImpl这样的具体类将在@Provides方法或具有@Inject注释的实际类上对其进行标记。

理想:标记您的实现@Singleton

@Singleton public class FooBarImpl implements Foo, Bar { /* ... */ }
// in your module:
@Binds Foo bindFoo(FooBarImpl impl);
@Binds Bar bindBar(FooBarImpl impl);
Run Code Online (Sandbox Code Playgroud)

这样,无论消费者要求Foo,Bar还是FooBarImpl,他们都将始终使用Dagger的内部双重检查锁定缓存实例(“ DoubleCheck”)。也可以标记Foo和Bar @Singleton,但这有点浪费,因为内部FooBarImpl提供程序总会始终返回相同的实例。无需在Foo Provider或Bar Provider上保留单独的副本。

如果FooBarImpl在控件的外部或外部,则可以@Singleton@Provides方法上进行标记。

不太理想:标记一个并绑定另一个

/* not Singleton */ public class FooBarImpl implements Foo, Bar { /* ... */ }
// in your module:
@Binds @Singleton Foo bindFoo(FooBarImpl impl);
@Binds Bar bindBar(Foo foo);
Run Code Online (Sandbox Code Playgroud)

假设Foo扩展了Bar,则可以将Bar绑定到Foo。在这里,单例实例保留在Foo提供程序中,注入FooBarImpl将返回新实例,并且Bar的注入每次都将咨询Foo(及其缓存提供程序)。

这听起来很严格糟糕,但也有有效的使用情况:试想一下,如果FooBarImpl是某种形式的CacheImpl,你想Foo和Bar一个实例,但返回@Named("accounts") ListeningCache,并@Named("accounts") Cache于都返回相同的不同实例。

不是您想要的:单独的单例绑定

为了完整性:

/* not Singleton */ public class FooBarImpl implements Foo, Bar { /* ... */ }
// in your module:
@Binds @Singleton Foo bindFoo(FooBarImpl impl);
@Binds @Singleton Bar bindBar(FooBarImpl impl);
Run Code Online (Sandbox Code Playgroud)

在这里,Foo和Bar将各自返回一个单独的Singleton实例,并且注入FooBarImpl每次将返回一个新实例。这对于需要两个或多个对象的用例非常有用,但是在尝试实现上面的“不太理想”的解决方案时,请小心不要意外地实现此解决方案。它们看起来确实很相似,但是行为却截然不同。