多个实例中的 Dagger 2 单例
Dagger 2 singleton in multiple instances
我刚刚测试了 Dagger 2,我对单例注释有一些奇怪的行为。我创建了一些测试代码来显示我的问题。
我的模块:
@Module
public class App {
@Provides
@Singleton
ThingA provideThingA(){
return new ConcreteThingA();
}
}
我想要的单例接口:
public interface ThingA {
void showMyId();
}
实施:
public class ConcreteThingA implements ThingA {
@Override
public void showMyId() {
System.out.println(this);
}
}
执行 Dagger 的代码:
public void doStuff() {
ThingA thingA=DaggerThingAComponent.create().provideThingA();
ThingA thingB=DaggerThingAComponent.create().provideThingA();
System.out.println("Hello");
}
这是一张截图,显示当我请求两次时我没有得到相同的实例。我错过了一些基本的东西吗? ThingA 只是一个愚蠢的名字,在我的实际应用程序中,我希望在我的服务中有这种单例行为。
诀窍是 Dagger 通过组件强制执行 scope/lifecycle,而您在这里创建了两个单独的组件:
ThingA thingA = DaggerThingAComponent.create().provideThingA();
ThingA thingB = DaggerThingAComponent.create().provideThingA();
每次您创建新的顶级@Singleton 注释组件时,Dagger 都会为每个@Singleton 对象创建一个全新的对象图和一个全新的容器。你应该有这个:
ThingAComponent component = DaggerThingAComponent.create();
ThingA thingA = component.provideThingA();
ThingA thingB = component.provideThingA(); // thingA == thingB
当然,通过依赖关系图进一步访问的任何内容都来自同一组件,因此这将保留您正在寻找的单例行为。
在大多数情况下,您不需要绕过组件:组件应该用于顶级组件,任何可以通过注入器访问的东西都应该 @Inject 它的依赖项(这意味着它不需要对组件本身的引用)。在迁移到 DI 或 Dagger 期间,这可能会出现问题,但创建多个 @Singleton 组件并不是绕过它的方法。相反,请尝试以下操作之一:
- 如果您需要某个东西的多个实例,无论您是否创建了
@Provides
方法,您总是可以注入 Provider<T>
而不是 T
。就此而言,如果您只需要特定依赖项的零个或一个副本,则可以注入 Lazy<T>
,尤其是在创建该对象特别繁重的情况下。
- 如果你需要在对象图的深处,你可以 @Inject 组件本身,尽管它总是比
@Inject Provider<T> tProvider
更可取,而不是 @Inject YourComponent
只是为了调用 YourComponent.getT
.
- 在某些情况下,包括 Android,将组件保存到全局可访问字段可能是有意义的,可以作为应用程序中的实例字段,也可以作为其他地方的静态字段。这特别是因为 Android 自己创建对象,反射性的,而不是从图中获取注入的实例。对于所有其他情况,注入您的依赖项以避免需要传递组件。
另请参阅:Bindings in the graph 来自 Dagger 2 用户指南
我刚刚测试了 Dagger 2,我对单例注释有一些奇怪的行为。我创建了一些测试代码来显示我的问题。
我的模块:
@Module
public class App {
@Provides
@Singleton
ThingA provideThingA(){
return new ConcreteThingA();
}
}
我想要的单例接口:
public interface ThingA {
void showMyId();
}
实施:
public class ConcreteThingA implements ThingA {
@Override
public void showMyId() {
System.out.println(this);
}
}
执行 Dagger 的代码:
public void doStuff() {
ThingA thingA=DaggerThingAComponent.create().provideThingA();
ThingA thingB=DaggerThingAComponent.create().provideThingA();
System.out.println("Hello");
}
这是一张截图,显示当我请求两次时我没有得到相同的实例。我错过了一些基本的东西吗? ThingA 只是一个愚蠢的名字,在我的实际应用程序中,我希望在我的服务中有这种单例行为。
诀窍是 Dagger 通过组件强制执行 scope/lifecycle,而您在这里创建了两个单独的组件:
ThingA thingA = DaggerThingAComponent.create().provideThingA();
ThingA thingB = DaggerThingAComponent.create().provideThingA();
每次您创建新的顶级@Singleton 注释组件时,Dagger 都会为每个@Singleton 对象创建一个全新的对象图和一个全新的容器。你应该有这个:
ThingAComponent component = DaggerThingAComponent.create();
ThingA thingA = component.provideThingA();
ThingA thingB = component.provideThingA(); // thingA == thingB
当然,通过依赖关系图进一步访问的任何内容都来自同一组件,因此这将保留您正在寻找的单例行为。
在大多数情况下,您不需要绕过组件:组件应该用于顶级组件,任何可以通过注入器访问的东西都应该 @Inject 它的依赖项(这意味着它不需要对组件本身的引用)。在迁移到 DI 或 Dagger 期间,这可能会出现问题,但创建多个 @Singleton 组件并不是绕过它的方法。相反,请尝试以下操作之一:
- 如果您需要某个东西的多个实例,无论您是否创建了
@Provides
方法,您总是可以注入Provider<T>
而不是T
。就此而言,如果您只需要特定依赖项的零个或一个副本,则可以注入Lazy<T>
,尤其是在创建该对象特别繁重的情况下。 - 如果你需要在对象图的深处,你可以 @Inject 组件本身,尽管它总是比
@Inject Provider<T> tProvider
更可取,而不是@Inject YourComponent
只是为了调用YourComponent.getT
. - 在某些情况下,包括 Android,将组件保存到全局可访问字段可能是有意义的,可以作为应用程序中的实例字段,也可以作为其他地方的静态字段。这特别是因为 Android 自己创建对象,反射性的,而不是从图中获取注入的实例。对于所有其他情况,注入您的依赖项以避免需要传递组件。
另请参阅:Bindings in the graph 来自 Dagger 2 用户指南