Dagger 2 问题覆盖单个提供来自应用程序使用的库中模块的注释方法

Dagger 2 issue overriding single provides annotated method from a module in a library which app uses

GitHub 项目 Link

我制作了一个 project on GitHub,它是我项目实际架构的 Dagger 2 架构模型。这个问题将基于 GitHub 项目。

我在这个问题中提供了很多代码片段,但是,在 Android Studio 中自己编译项目可能更容易理解问题。

如果您检查代码,它不会编译。进入 AppModule.java 并注释掉提供的方法,它应该可以编译。

主要问题是post的最后一行。

https://github.com/qazimusab/Dagger2LibraryProject

建筑

我有一个库,其中包含制作应用程序所需的所有代码。这种架构的要点是我在项目中创建的每个应用程序都应该能够使用该库,并且通过 dagger 2,能够为它想要的任何单个 class 或 activity 提供不同的实现它是自己的模块。此时我在这个示例项目中只有一个使用该库的应用程序。

问题

对于 dagger one,我有相同的架构,并且在特定于应用程序的模块(与库模块相对)中,我能够添加一个新的提供注释的方法来覆盖在任何库模块,只要

  1. 该方法在应用程序模块中的一个模块中
  2. 该方法用@Provides 注释
  3. 该方法与您要覆盖的方法具有相同的 return 类型

对于 Dagger 2,当我不覆盖任何提供或者如果我这样做时,当我覆盖该模块中的每个提供并从应用程序特定模块的包含中删除该模块时,该体系结构工作。

例如,在我的项目中,我有一个应用程序和一个库。

该应用有一个AppModule;该库有一个 CatModule 来提供 Cat 和 CatFood,一个 dog 模块来提供 Dog 和 DogFood,还有一个 LibraryModule 来提供活动。

CatModule.java

package com.example.qaziahmed.library.application.modules;

import com.example.qaziahmed.library.classes.Cat; import
com.example.qaziahmed.library.classes.CatFood; import
com.example.qaziahmed.library.classes.contract.ICat; import
com.example.qaziahmed.library.classes.contract.ICatFood;

import javax.inject.Singleton;

import dagger.Module; import dagger.Provides;

/**  * Created by qaziahmed on 11/23/15.  */ @Module public class
CatModule {

    @Provides
    @Singleton
    ICat provideCat() {
        return new Cat();
    }

    @Provides
    ICatFood provideCatFood(){
        return new CatFood();
    } }

DogModule.java

package com.example.qaziahmed.library.application.modules;

import com.example.qaziahmed.library.classes.Dog; import
com.example.qaziahmed.library.classes.DogFood; import
com.example.qaziahmed.library.classes.contract.IDog; import
com.example.qaziahmed.library.classes.contract.IDogFood;

import javax.inject.Singleton;

import dagger.Module; import dagger.Provides;

/**  * Created by qaziahmed on 11/23/15.  */ @Module public class
DogModule {

    @Provides
    @Singleton
    IDog provideDog() {
        return new Dog();
    }

    @Provides
    IDogFood provideDogFood(){
        return new DogFood();
    }

}

因此,在我的应用程序模块中,我想提供 ICat 的家猫实现而不是通用猫,以及 IDogFood 的 AllNaturalDogFood 实现而不只是常规 DogFood,然后在我的 AppModule 中我添加两个提供来覆盖它们

AppModule.java

package com.example.qaziahmed.dagger2libraryproject.application;

import
com.example.qaziahmed.dagger2libraryproject.classes.AllNaturalDogFood;
import com.example.qaziahmed.dagger2libraryproject.classes.HouseCat;
import com.example.qaziahmed.library.application.modules.CatModule;
import com.example.qaziahmed.library.application.modules.DogModule;
import
com.example.qaziahmed.library.application.modules.LibraryModule;
import com.example.qaziahmed.library.classes.contract.ICat; import
com.example.qaziahmed.library.classes.contract.IDogFood;

import javax.inject.Singleton;

import dagger.Module; import dagger.Provides;

/**  * Created by ogre on 2015-07-12  */ @Module(includes = {
        LibraryModule.class,
        DogModule.class,
        CatModule.class }) public class AppModule {

    @Provides
    @Singleton
    ICat provideHouseCat() {
        return new HouseCat();
    }

    @Provides
    IDogFood provideAllNaturalDogFood(){
        return new AllNaturalDogFood();
    } }

现在,当我 运行 这个设置时,这是我得到的错误:

Error:com.example.qaziahmed.library.classes.contract.ICat is bound multiple times: @Provides @Singleton com.example.qaziahmed.library.classes.contract.ICat com.example.qaziahmed.dagger2libraryproject.application.AppModule.provideHouseCat() @Provides @Singleton com.example.qaziahmed.library.classes.contract.ICat com.example.qaziahmed.library.application.modules.CatModule.provideCat() Error:com.example.qaziahmed.library.classes.contract.IDogFood is bound multiple times: @Provides com.example.qaziahmed.library.classes.contract.IDogFood com.example.qaziahmed.dagger2libraryproject.application.AppModule.provideAllNaturalDogFood() @Provides com.example.qaziahmed.library.classes.contract.IDogFood com.example.qaziahmed.library.application.modules.DogModule.provideDogFood()

现在,如果在 AppModule.java 中,我还添加了 provides annotated 方法来提供 Cat Food 和 Provide Dog,然后从 App Module 的 include 中删除 CatModule.class 和 DogModule.class 然后它作品。

但是,整个问题是如何覆盖库中某个模块中的单个提供方法,而不必覆盖该特定模块内的每个提供注释方法,然后从包含中删除该模块在 AppModule.java

问题是您的注入看到两个提供相同对象的方法。

如果您阅读此 link:http://google.github.io/dagger/ 您可以通过将您的提供者命名为:

来解决这个问题
@Provides @Named("water")

然后,在您的注入中,像这样引用它:

@Inject @Named("water")

将尝试从 Dagger 2 文档中解密此引用:

Dagger 2 doesn't support overrides. Modules that override for simple testing fakes can create a subclass of the module to emulate that behavior. Modules that use overrides and rely on dependency injection should be decomposed so that the overridden modules are instead represented as a choice between two modules.

在您当前的示例中,您不依赖依赖注入,因为您的 provides* 方法创建简单的新对象,因此您将能够创建模块的子类,覆盖 provides您需要覆盖的方法,然后在您的组件中包含该新模块。

当你依赖 DI 时(实际上你会在项目的某个阶段)像这样:

@Provides
@Singleton
ICat provideCat(IBowtie bowtie) { // 'bowtie' needs to be injected
    return new CatWithBowtie(Bowtie);
}

它涉及到 "Modules that use overrides and rely on dependency injection should be decomposed" 这基本上意味着:你必须将 CatModule 一分为二:CatModule 仅包含 providesCat 和 'CatFoodModule' 包含 provideCatFood()。然后您的应用程序组件您只需使用新的 CatWithBowtieModule 而不是 CatModule.

有两个有用的建议:

  1. 在库项目中拆分模块,因此每个模块只有一个 provides* 方法。是的,这听起来像废话,但这是稍后在您的应用程序中提供轻松覆盖的唯一方法。

  2. 暂时假设该库是第三方作为 JAR/AAP 提供给您的,而您甚至没有源代码。在那种情况下,您将无法重用库中定义的模块,因此您必须自己创建所有模块。这正是 Dagger 2 所发生的事情。

当您尝试在您的应用程序中直接使用您的库中的模块时(就像您所做的那样),这两个项目不再是两个独立的项目,而是一个看起来像两个项目的项目(它们是 clusterf*ck 紧密耦合的) ).应用程序依赖库是可以的,但库依赖应用程序是。归结为:在Dagger 2中最好不要使用跨(项目)边界modulescomponents.

有人可能会问:"What is the good of using Dagger 2 in a lib if I can't use lib's modules/components in my app?!"。好吧,你仍然可以在你的单元测试中使用你的 dagger modules/components 这毕竟是使用 Dagger 的主要好处。此外,如果您的 lib 打算供其他人使用,您可以(必须?)提供一个参考应用程序,该应用程序展示了如何 "wire" 事情,以便 lib 的用户能够在适合他们或至少适合他们的情况下复制该代码看看如何开始。

作为解决方法,我编写了两种方法,一种用于 @Provides,一种用于 @Overrides

@Override
protected X getX() {
    return new X();
}

@Provides
X provideX() {
    return getX();
}