我是Dagger 2的新手.我有这个场景,我不想在我的应用程序中注入一个对象(在演示者中,在api中)
我最初没办法提供它.在我的应用程序的某个阶段进行身份验证之后才会创建它.
来自文档http://google.github.io/dagger/
我看到Lazy加载可能是一种解决这个问题的方法,例如
@Inject LazylazyGrinder;
然后使用以下方法获取这样的值:lazyGrinder.get().grind();
我的问题是:
我可以安全地用新的对象交换对象吗?
有没有其他推荐的方法来做到这一点?
谢谢
这不是一个很好的匹配Lazy
.Lazy
是一种延迟昂贵的对象初始化的好方法,但它暗示了一些你不想要或不需要的语义,特别是关于你想要的"安全交换"行为.
简而言之,Lazy是一个在本地进行memoizes的Provider包装器:
如果你从不打电话get
,Dagger永远不会创造有问题的对象.
第一次调用get
创建和存储对象实例.
第二次调用get
返回相同的实例,依此类推,无论对象是否标记为Singleton.
这使得Lazy成为昂贵对象的绝佳选择,否则该对象将成为一个领域(但可能永远不会被使用).但是,如果引用可能会改变(按照您的意愿),Lazy将会让人感到困惑:它会在第一次使用时存储该值并且永远不会在本地更新,因此多个过时的副本可能会在您的应用程序中浮动什么是"正确的"价值在任何给定的时间.
从您的示例中借用Grinder,更好的解决方案包括:
使用@Provides
返回模块中的字段的方法,可以在以后更新.您需要Provider
为每个长期存在的对象实例注入,因为仅对Grinder的注入引用不会更新.如果你有很多短命的物体,这仍然是最好的选择.
引用是隐式单例,但没有注释,因为您自己控制实例.Dagger会getGrinder
经常调用你的方法.
@Module public class YourModule { private Grinder grinder; public void setGrinder(Grinder grinder) { this.grinder = grinder; } @Provides public Grinder getGrinder() { return grinder; } } /* elsewhere */ YourModule module = new YourModule(); YourComponent component = DaggerYourComponent.builder() .yourModule(module) .build(); /* ... */ module.setGrinder(latestAndGreatestGrinder);
正如EpicPandaForce在评论中提到的,创建/绑定一个单独的GrinderHolder,GrinderController或AtomicReference对象,它提供当前实例并允许更新.这样就不可能直接注入Grinder,但是注入获取当前正确Grinder的对象是很容易和明显的.如果您的单个GrinderHolder实现在第一次请求之前不创建Grinder,那么您已经有效地创建了一个Lazy单例.
如果在创建组件时无法提供该对象,请不要将其添加到“组件”图中!那就是要求混乱的图形依赖和不一致。对于您正在考虑的问题@Subcomponent
,更好的解决方案是一种方法,该方法允许您创建一个新组件,该组件可以继承父组件的依赖关系,还可以添加新组件。这是一个例子:
@Component
interface RegularComponent {
@AppInstanceId String appInstanceId(); // unique per app install; not related to logging in
AuthenticatedComponent newAuthenticatedComponent();
}
@Subcomponent
interface AuthenticatedComponent {
Set friends();
@AccountId String accountId();
}
在此,@AccountId
子组件中的可以使用appInstanceId
提供帐户ID(如果需要),因为子组件与其父组件共享依赖项。
如果您需要为子组件的模块提供状态(带有accountId,auth令牌等),请随时将其作为参数传递给@Module
并将其存储在private final
字段中。您可以在文档中阅读有关如何提供子组件模块的更多信息。