在Android上使用Dagger 2进行懒惰注射

And*_*ast 11 android dependency-injection lazy-initialization dagger-2

我是Dagger 2的新手.我有这个场景,我不想在我的应用程序中注入一个对象(在演示者中,在api中)

我最初没办法提供它.在我的应用程序的某个阶段进行身份验证之后才会创建它.

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

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

@Inject 
Lazy<Grinder> lazyGrinder;
Run Code Online (Sandbox Code Playgroud)

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

我的问题是:

  • 我可以安全地用新的对象交换对象吗?
  • 有没有其他推荐的方法来做到这一点?

谢谢

Jef*_*ica 23

这不是一个很好的匹配Lazy.Lazy是一种延迟昂贵的对象初始化的好方法,但它暗示了一些你不想要或不需要的语义,特别是关于你想要的"安全交换"行为.

简而言之,Lazy是一个在本地进行memoizes的Provider包装器:

  • 如果你从不打电话get,Dagger永远不会创造有问题的对象.
  • 第一次调用get创建和存储对象实例.
  • 第二次调用get返回相同的实例,依此类推,无论对象是否标记为Singleton.

这使得Lazy成为昂贵对象的绝佳选择,否则该对象将成为一个领域(但可能永远不会被使用).但是,如果引用可能会改变(按照您的意愿),Lazy将会让人感到困惑:它会在第一次使用时存储该值并且永远不会在本地更新,因此多个过时的副本可能会在您的应用程序中浮动什么是"正确的"价值在任何给定的时间.


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

  • 使用@Provides返回模块中的字段的方法,可以在以后更新.您需要Provider<Grinder>为每个长期存在的对象实例注入,因为仅对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);
    
    Run Code Online (Sandbox Code Playgroud)
  • 正如EpicPandaForce在评论中提到的,创建/绑定一个单独的GrinderHolder,GrinderController或AtomicReference对象,它提供当前实例并允许更新.这样就不可能直接注入Grinder,但是注入获取当前正确Grinder的对象是很容易和明显的.如果您的单个GrinderHolder实现在第一次请求之前不创建Grinder,那么您已经有效地创建了一个Lazy单例.


小智 5

如果在创建组件时无法提供该对象,请不要将其添加到“组件”图中!那就是要求混乱的图形依赖和不一致。对于您正在考虑的问题@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();
}
Run Code Online (Sandbox Code Playgroud)

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

如果您需要为子组件的模块提供状态(带有accountId,auth令牌等),请随时将其作为参数传递给@Module并将其存储在private final字段中。您可以在文档中阅读有关如何提供子组件模块的更多信息