将非活动类添加到Dagger 2 Graph Android

时间:2015-04-14 00:11:11

标签: android dagger-2

在我见过的有限例子之外,我很难绕过如何使用Dagger 2.0。我们来看一个阅读应用程序的例子。在这个阅读应用程序中,有一个用户的故事库和登录的能力。为了这个例子,感兴趣的类是:

MainApplication.java - 扩展应用

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

调用的

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

我仍然试图围绕我应该创建的组件和模块。到目前为止,我可以收集到的内容:

创建一个提供HelperModuleAccountManager实例的LibraryManager

@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为空,我不明白为什么或如何解决问题。我之所以认为这是因为注入图表的LibraryManager没有直接使用AccountManager,但是我需要将{{1}}注入图有些怎么样?

3 个答案:

答案 0 :(得分: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,你需要以某种方式捕获组件。有很多方法可以实施。这只是一个例子,让你知道它是如何工作的。 现在你可以摧毁火星:)

答案 1 :(得分: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中有很多参数,除了构造函数注入之外,你还可以使用方法注入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();
        ...
    }
}

在实例化对象后执行方法注入。但是,这种方法注入的用例无效,尝试更喜欢构造函数或字段注入。

答案 2 :(得分:1)

我想我已经找到了一个很好的解决方案。我没有尝试将AccountManager注入LibraryManager,而是在AccountManager中提供MainApplicationComponent并以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(); }

MainApplicationComponent:

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

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

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

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

public class LibraryManager {

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

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

HasComponent<MainApplicationComponent>

最后在HelperModule中我们只将@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的构造函数:

LibraryManager

这也应该使单元测试变得非常容易。如果我正在对AccountManager进行单元测试并希望模拟TestMainApplicationComponent我只需创建一个扩展MainApplicationComponent的TestHelperModule并在其中包含AccountManager列表模块将提供模拟TestMainApplicationComponent并将LibraryManager传递给{{1}}的构造函数。

我是Dagger的新手,所以我可能会遗漏一些东西但是我已经尝试了除了单元测试之外的所有东西,但它似乎到目前为止还在工作。将很快发布一个GitHub链接,为那些感兴趣的人提供单元测试示例。

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