4

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

MainApplication.java- 扩展应用程序

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

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

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

创建一个HelperModule提供AccountManagerLibraryManager实例的:

@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);
}

包含@Injects LibraryManager libraryManager在图中MainApplication并将应用程序注入到图中。最后,它查询注入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();
        ...
    }
}

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

4

3 回答 3

7

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

你的 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,您需要以某种方式捕获组件。有很多方法可以实现。这只是一个说明,让您了解它是如何工作的。现在你可以摧毁火星了:)

于 2015-11-16T10:55:42.630 回答
2

您可以直接在提供方法中满足依赖关系:

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

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

@Signleton
public class LibraryManager {
    private final AccountManager accountManager;

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

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

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

如果您有很多参数LibraryManager,除了构造函数注入之外,您还可以对设置器使用方法注入:

@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();
        ...
    }
}

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

于 2015-04-14T03:47:36.663 回答
1

我想我已经想出了一个很好的解决方案。我没有尝试将 in 注入 ,而是AccountManager提供LibraryManagerinAccountManagerMainApplicationComponentLibraryManager这种方式访问​​。

主应用组件:

@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<MainApplicationComponent>给 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我可以简单地创建一个TestMainApplicationComponent扩展 MainApplicationComponent 并TestHelperModule在它的模块列表中包含一个,它将提供一个模拟AccountManager并将 传递TestMainApplicationComponentLibraryManager的构造函数。

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

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

于 2015-04-14T19:53:41.893 回答