【问题标题】:Lazy Injection with Dagger 2 on Android在 Android 上使用 Dagger 2 进行惰性注入
【发布时间】:2016-03-07 02:40:24
【问题描述】:

我是 Dagger 2 的新手。我有这种情况,我不想在我的应用程序中注入对象(在演示者中,在 api 中)

我最初无法提供它。直到在我的应用程序的某个阶段进行身份验证后才会创建它。

来自文档http://google.github.io/dagger/

我看到延迟加载可能是解决这个问题的一种方法,例如

@Inject 
Lazy<Grinder> lazyGrinder;

然后使用以下方法获取值: lazyGrinder.get().grind();

我的问题是:

  • 之后我可以安全地将对象换成新对象吗?
  • 还有其他推荐的方法吗?

谢谢

【问题讨论】:

  • 覆盖是什么意思?
  • @cyroxis 我的意思是交换,问题已更新
  • 我只用过一次Lazy&lt;T&gt;。它让我在启动时陷入僵局。再也不。顺便说一句,我不知道我做错了什么。我个人只会创建一个单例Holder 对象,如GrinderHolder 并在你拥有它时设置它(否则null
  • Dagger 提供程序是延迟加载的,这意味着只要您不使用该对象,它就不会启动。

标签: android dependency-injection lazy-initialization dagger-2


【解决方案1】:

这不适合LazyLazy 是延迟昂贵的对象初始化的好方法,但它暗示了一些你不想要或不需要的语义,特别是关于你想要的“安全交换”行为。

简单地说,Lazy 是一个 Provider 包装器,它在本地 memoizes

  • 如果您从不调用 get,Dagger 将永远不会创建有问题的对象。
  • 第一次调用get 创建并存储对象实例。
  • 第二次调用 get 返回相同的实例,以此类推,无论对象是否标记为 Singleton。

这使得 Lazy 成为昂贵对象的绝佳选择,否则它将成为一个字段(但可能永远不会使用)。但是,如果引用可能会更改(如您所愿),Lazy 只会令人困惑:它会在第一次使用时存储值并且永远不会在本地更新,因此多个过期副本可能会在您的应用程序中浮动在任何给定时间的“正确”值是什么。


从您的示例中借用 Grinder,更好的解决方案包括:

  • 使用@Provides 方法返回模块中的字段,以后可以更新。您需要为每个长期存在的对象实例注入Provider&lt;Grinder&gt;,因为单独注入对 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);
    
  • 正如 cmets 中提到的 EpicPandaForce,创建/绑定提供当前实例并允许更新的单例 GrinderHolder、GrinderController 或 AtomicReference 对象。这样就不可能直接注入 Grinder,但注入获取当前正确 Grinder 的对象很容易且显而易见。如果您的单例 GrinderHolder 实现直到您第一次请求时才创建 Grinder,那么您实际上已经自己创建了一个惰性单例。

【讨论】:

  • 这里的文档,带有示例和解释:google.github.io/dagger/api/2.0/dagger/Lazy.html
  • 但是,如果我想在程序启动时实例化整个对象图,但可以在运行时构建某些依赖关系,该怎么办?有没有办法在运行时将它们“添加”到依赖容器中,以便我可以在其他地方懒惰地使用它们与Lazy&lt;T&gt;#get?或者我如何能够使用在运行时通过 dagger 在我的程序周围创建的依赖项?
  • @cobby:这听起来像是一个新问题,而不是后续问题,我鼓励您在顶层询问详细信息(并可能在此处链接此对话)。我确实认为注入 Lazy 或 Provider 对您来说是合理的;这是我要警告的“安全交换”行为,因为依赖注入框架对于获取可以更改的“最新”值并不是很好。 Dagger 是编译时的,所以如果你想在运行时添加值,你需要在编译时检测它们,并且只在运行时 get 它们,或者使用稍后创建的子组件(等)。
【解决方案2】:

如果您在创建组件时无法提供对象,请不要将其添加到您的组件图中!这要求混淆图形依赖和不一致。您正在考虑的一个更好的解决方案是@Subcomponent 方法,它允许您创建一个新组件,该组件继承父项的依赖项,但也添加新组件。这是一个例子:

@Component
interface RegularComponent {
  @AppInstanceId String appInstanceId(); // unique per app install; not related to logging in
  AuthenticatedComponent newAuthenticatedComponent();
}

@Subcomponent
interface AuthenticatedComponent {
  Set<Friend> friends();
  @AccountId String accountId();
}

这里,子组件中的@AccountId 可以使用appInstanceId 来提供帐户ID(如果需要),因为子组件与其父组件共享依赖关系。

如果您需要为子组件的模块提供状态(使用 accountId、auth 令牌等),请随时将其作为参数传递给 @Module 并将其存储在 private final 字段中。你可以阅读更多关于如何提供子组件模块in the documentation

【讨论】:

  • 只有在创建父组件的相同位置创建子组件时才有可能(只有这样我才能重用来自父组件的完全相同的运行时实例)。如果我需要在我的代码中完全不同的位置来自父组件的依赖项(创建newcomponent 也会给我新的实例)?我会以某种方式需要一个全局容器,从中获取此依赖项,而 Dagger 中不存在该容器?所以,问题仍然存在:如何在我的程序周围使用在运行时创建的依赖项?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-21
相关资源
最近更新 更多