【问题标题】:Adding Non Activity Classes to Dagger 2 Graph Android将非活动类添加到 Dagger 2 Graph Android
【发布时间】:2015-11-16 19:01:54
【问题描述】:

除了我见过的有限示例之外,我很难思考如何使用 Dagger 2.0。让我们以阅读应用程序为例。在这个阅读应用程序中,有一个用户故事库和登录功能。本示例感兴趣的类别是:

MainApplication.java - 扩展应用程序

LibraryManager.java - 负责在用户库中添加/删除故事的经理。这是从MainApplication 调用的

AccountManager.java - 负责保存所有用户登录信息的管理器。可以从 LibraryManager 中调用

我仍在努力思考应该创建哪些组件和模块。到目前为止,我可以收集到以下信息:

创建一个提供AccountManagerLibraryManager 实例的HelperModule

@Module
public class HelperModule {

    @Provides
    @Singleton
    AccountManager provideAccountManager() {
        return new AccountManager();
    }

    @Provides
    @Singleton
    LibraryManager provideLibraryManager() {
        return new LibraryManager();
    }

}

创建一个MainApplicationComponent,在其模块列表中列出HelperModule

@Singleton
@Component(modules = {AppModule.class, HelperModule.class})
public interface MainApplicationComponent {
    MainApplication injectApplication(MainApplication application);
}

MainApplication 中包含@Injects LibraryManager libraryManager 并将应用程序注入到图中。最后它查询注入的LibraryManager 库中的故事数:

public class MainApplication extends Application {

    @Inject LibraryManager libraryManager;

    @Override
    public void onCreate() {
        super.onCreate();

        component = DaggerMainApplicationComponent.builder()
                .appModule(new AppModule(this))
                .helperModule(new HelperModule())
                .build();
        component.injectApplication(this);

        // Now that we have an injected LibraryManager instance, use it
        libraryManager.getLibrary();
    }
}

AccountManager注入LibraryManager

public class LibraryManager {
    @Inject AccountManager accountManager;

    public int getNumStoriesInLibrary() {
        String username = accountManager.getLoggedInUserName();
        ...
    }
}

但是问题是当我尝试在LibraryManager 中使用AccountManager 时它为空,我不明白为什么或如何解决这个问题。我在想这是因为注入图表的MainApplication 没有直接使用AccountManager,但是我需要如何将LibraryManager 注入图表?

【问题讨论】:

  • 顺便说一下,由于它没有参数,因此您不需要在组件构建器中包含 HelperModule。
  • @steffandroid 我也是这么想的,但是为什么 LibraryManager 中的 AccountManager 没有被初始化呢?

标签: android dagger-2


【解决方案1】:

如下修改你的类,它会起作用:

你的 POJO:

public class LibraryManager {
    @Inject AccountManager accountManager;

    public LibraryManager(){
        MainApplication.getComponent().inject(this);
    }

    public int getNumStoriesInLibrary() {
        String username = accountManager.getLoggedInUserName();
        ...
    }

    ...
}

你的组件接口:

 @Singleton
 @Component(modules = {AppModule.class, HelperModule.class})
    public interface MainApplicationComponent {
        void inject(MainApplication application);
        void inject(LibraryManager lm);
    }
 }

你的应用类:

public class MainApplication extends Application {
    private static MainApplicationComponent component;

    @Inject LibraryManager libraryManager;

    @Override
    public void onCreate() {
        super.onCreate();

        component = DaggerMainApplicationComponent.builder()
                .appModule(new AppModule(this))
                .helperModule(new HelperModule())
                .build();
        component.injectApplication(this);

        // Now that we have an injected LibraryManager instance, use it
        libraryManager.getLibrary();
    }

    public static MainApplicationComponent getComponent(){return component;}


}

实际上,您需要对所有依赖类执行相同的操作,基本上您可以访问所有 Activity 子类中的应用程序类,因此将 get 组件设置为静态方法也不会少。但是对于 POJO,您需要以某种方式捕获组件。有很多方法可以实现。这只是一个说明,让您了解它是如何工作的。 现在你可以摧毁火星了:)

【讨论】:

  • 这就是我们的做法,到目前为止它一直在发挥作用。
【解决方案2】:

你可以直接在provide方法中满足依赖:

@Provides
@Singleton
LibraryManager provideLibraryManager(AccountManager accountManager) {
    return new LibraryManager(accountManager);
}

或者使用构造函数注入(从HelperModule中移除provideLibraryManager()方法):

@Signleton
public class LibraryManager {
    private final AccountManager accountManager;

    @Inject
    public LibraryManager(AccountManager accountManager) {
      this.accountManager = accountManager
    }

    public int getNumStoriesInLibrary() {
        String username = accountManager.getLoggedInUserName();
        ...
    }
}

使用构造函数注入创建的对象是自动提供的。

如果LibraryManager中有很多参数,除了构造函数注入之外,你还可以使用setter方法注入:

@Singleton
public class LibraryManager {
    private final AccountManager accountManager;
    private SomeManager someManager;

    @Inject
    public LibraryManager(AccountManager accountManager) {
      this.accountManager = accountManager
    }

    @Inject
    public setSomeManager(SomeManager someManager) {
       this.someManager = someManager
    }

    public int getNumStoriesInLibrary() {
        String username = accountManager.getLoggedInUserName();
        ...
    }
}

方法注入在对象被实例化后执行。但是这个方法注入的用例是无效的,尽量选择构造函数或者字段注入。

【讨论】:

  • 谢谢,但我真的不想要构造函数注入,因为构造函数会很大。在我的实际应用中,有 >10 个管理器/数据库适配器/帮助器类/等。 LibraryActivity 处理并将它们全部放在构造函数中会变得不守规矩。
  • 如果你想提供LibraryManager作为依赖,你需要使用构造函数注入或者手动创建对象并满足模块中相应提供方法中的所有依赖。或者您可以使用方法注入作为其他 >10 个参数的设置器,但实际上它不是有效的用例。我可以通过方法注入更新我的答案。
  • “手动创建对象并满足模块中相应提供方法中的所有依赖项”是什么意思?我正在这样做,不是我,而是有两个@Provides 方法,一个用于LibraryManager,一个用于AccountManager?你能举例说明你的意思吗?
  • 在提供方法中,您不能将AccountManager 注入到LibraryManager。因为组件还不存在。我的意思是,如果构造函数注入不适合您,您可以使用new LibraryManager 创建 libraryManager,然后使用 setter 设置依赖关系,例如libraryManager.setAccountManager(accountManager) 并作为依赖项返回。但那很脏。在您调用component.inject(libraryManager)(字段注入)或执行构造函数注入或构造函数+方法注入之前,不会发生实际注入。我将使用方法注入更新答案以更清楚。
  • 感谢更新的答案。方法注入方法看起来会起作用。 “这种方法注入的用例无效”是什么意思?
【解决方案3】:

我想我想出了一个很好的解决方案。我没有尝试将AccountManager 注入LibraryManager,而是在MainApplicationComponent 中提供AccountManager 并以这种方式从LibraryManager 访问。

主应用组件:

@Singleton
@Component(modules = {AppModule.class, HelperModule.class})
public interface MainApplicationComponent {
    MainApplication injectApplication(MainApplication application);

    // Provide the managers here so all classes that have a pointer to the MainApplicationComponent can access them.
    // This avoids having to pass each manager to the constructor of all classes that need them
    AccountManager accountManager();
    ArchiveManager archiveManager();
}

使用示例 Android 应用获得灵感 (https://github.com/gk5885/dagger-android-sample) 我创建了一个 HasComponent 接口:

public interface HasComponent<C> {
    C getComponent();
}

并让 MainApplication 实现接口。此外,在创建 HelperModule 时,您会注意到它传递了这个,因此模块可以访问组件:

public class MainApplication extends Application implements HasComponent<MainApplicationComponent>{

    MainApplicationComponent mainApplicationComponent;

    @Override
    public MainApplicationComponent getComponent() {
        return mainApplicationComponent;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        component = DaggerMainApplicationComponent.builder()
                .appModule(new AppModule(this))
                .helperModule(new HelperModule(this))
                .build();
        component.injectApplication(this);

        // Now that we have an injected LibraryManager instance, use it
        mainApplicationComponent.libraryManager().getLibrary();
    }
}

LibraryManager 已更改,因此它将 HasComponent 作为构造函数中的参数:

public class LibraryManager {

    AccountManager accountManager;
    public ArchiveManager(HasComponent<MainApplicationComponent> hasComponent) {
        accountManager = hasComponent.getComponent().accountManager();
    }
    ...
}

最后在 HelperModule 中,我们只需将 HasComponent&lt;MainApplicationComponent&gt; 的实现传递给 LibraryManager 的构造函数:

@Module
public class HelperModule {

private HasComponent<WattpadComponent> hasComponent;

public HelperModule(HasComponent<WattpadComponent> hasComponent) {
    this.hasComponent = hasComponent;
}

@Provides
@Singleton
AccountManager provideAccountManager() {
    return new AccountManager(hasComponent);
}

@Provides
@Singleton
ArchiveManager provideLibraryManager() {
    return new LibraryManager(hasComponent);
}

}

这也应该使单元测试变得非常容易。如果我对LibraryManager 进行单元测试并想模拟AccountManager,我可以简单地创建一个扩展MainApplicationComponent 的TestMainApplicationComponent,并在它的模块列表中包含一个TestHelperModule,它将提供一个模拟的AccountManager 并通过TestMainApplicationComponentLibraryManager 的构造函数。

我是 Dagger 的新手,所以我可能会遗漏一些东西,但我已经尝试了除单元测试之外的所有内容,并且到目前为止它似乎工作正常。不久将发布一个 GitHub 链接,其中包含单元测试示例,供感兴趣的人参考。

感谢@Kirill 的回答,以便更好地理解组件如何实例化对象。

【讨论】:

  • 嗯,看起来很不寻常。这对你有用吗?但是如果发生循环依赖怎么办?据我所知,它可以破坏一切,因为组件依赖于模块来创建依赖项。但是你把它倒过来了:依赖依赖于组件来创建自己。如果AccountManager 请求LibraryManager,反之亦然呢?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-12
  • 1970-01-01
  • 2019-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-16
相关资源
最近更新 更多