【问题标题】:Dagger 2: how to change provided dependencies at runtimeDagger 2:如何在运行时更改提供的依赖项
【发布时间】:2016-12-11 08:37:33
【问题描述】:

为了学习 Dagger 2,我决定重写我的应用程序,但我一直在为以下问题寻找合适的解决方案。

出于本示例的目的,假设我们有一个名为 Mode 的接口:

public interface Mode {
    Object1 obj1();

    //some other methods providing objects for app
}

和两个实现: NormalMode 和 DemoMode。

模式存储在单例中,因此可以从应用程序中的任何位置访问。

public enum ModeManager {
  INSTANCE,;

  private Mode mode;

  public Mode mode() {
    if (mode == null)
      mode = new NormalMode();
    return mode;
  }

  public void mode(Mode mode) { //to switch modules at runtime
    this.mode = mode;
  }
}

NormalMode 在运行时切换为DemoMode(比如说,当用户在后台单击cs几次时)

public void backgroundClicked5Times(){
  ModeManager.INSTANCE.mode(new DemoMode());
  //from now on every object that uses Mode will get Demo implementations, great!
}

所以首先我摆脱了单例并将模式定义为 Dagger 2 模块:

@Module
public class NormalModeModule {
  @Provides
  public Object1 provideObject1() {
    return new NormalObject1();
  }
}

@Module
public class DemoModeModule {
  @Provides
  public Object1 provideObject1() {
    return new DemoObject1();
  }
}

现在在 backgroundClicked5Times 方法中,我想在 DAG 中用 DemoModeModule 替换 NormalModeModule 而不是处理单例,这样其他需要 Object1 的类将从现在开始获得 DemoObject1 实现。

如何在 Dagger 中做到这一点?

提前致谢。

【问题讨论】:

标签: dependency-injection dagger-2


【解决方案1】:

用匕首试验了一段时间,我想出了在我的用例中运行良好的解决方案。

  1. 定义将保存有关模式的状态信息的类

    public class Conf {
      public Mode mode;
    
      public Conf(Mode mode) {
        this.mode = mode;
      }
    
      public enum Mode {
        NORMAL, DEMO
      }
    }
    
  2. 在模块中提供Conf 的单例实例

    @Module
    public class ConfModule {
      @Provides
      @Singleton
      Conf provideConf() {
        return new Conf(Conf.Mode.NORMAL);
      }
    }
    
  3. 将模块添加到 AppComponent

    @Singleton
    @Component(modules = {AppModule.class, ConfModule.class})
    public interface AppComponent {
        //...
    }
    
  4. 根据Mode定义提供不同对象的模块

    @Module
    public class Object1Module {
    
      @Provides
      Object1 provideObject1(Conf conf) {
        if (conf.mode == Conf.Mode.NORMAL)
          return new NormalObject1();
        else
          return new DemoObject1();
      }
    }
    
  5. 要在运行时切换模式,只需注入 Conf 对象并修改它:

    public class MyActivity extends Activity {
        @Inject Conf conf;
    
        //...
    
        public void backgroundClicked5Times(){
            conf.mode = Conf.Mode.DEMO;
    
            //if you have dagger objects in this class that depend on Mode
            //execute inject() once more to refresh them
        }
    }
    

【讨论】:

  • 似乎是一种不错的方法,但是当提供的依赖项(应在运行时替换)是单例时,这将不起作用,对吗?
  • 是的,如果您将 provideObject1 注释为 @Singleton 它将不起作用,但另一方面它与此用例中的想法相矛盾。如果你想在运行时替换对象,那么你不能用@Singleton 标记这样的方法。另一方面,您可以创建 NormalObject1 和 DemoObject1 单例,这样提供依赖项的方法就不会创建超过 2 个实例。
  • 最后一句话是我需要的信息。很好的解决方案,谢谢 :)
  • 这正是我想要的。但是您能否详细说明最后一条评论 - “如果您在此类中有依赖模式的 dagger 对象,请再次执行 inject() 以刷新它们”。
  • 我喜欢这个想法,把模块当作一个配置
【解决方案2】:

也许你可以考虑使用多重绑定?

@Module
public class NormalModeModule {
  @Provides
  @IntoMap
  @StringKey("normal")
  public Object1 provideObject1() {
    return new NormalObject1();
  }
}

@Module
public class DemoModeModule {
  @Provides
  @IntoMap
  @StringKey("demo")
  public Object1 provideObject1() {
    return new DemoObject1();
  }
}

当使用模式时:

@Inject
Map<String, Mode> modes;
//or you perfer lazy initialization:
Map<String, Provider<Mode>> modes;

public void backgroundClicked5Times(){
  ModeManager.INSTANCE.mode(modes.get("demo"));
  //if you are using Provider:
  ModeManager.INSTANCE.mode(modes.get("demo").get());
  //from now on every object that uses Mode will get Demo implementations, great!
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多