如何将完成= false的模块中的缺失注入从Dagger 1迁移到Dagger 2

时间:2015-07-11 07:43:00

标签: android dependency-injection dagger dagger-2

我有一个图书馆项目/模块,Android应用程序和常规Java应用程序都使用它。 在Dagger 1中,该项目/模块具有属性complete = false。在@Inject字段中,任何类实现或@Provides方法都不满足。我们的想法是强制拥有complete = true的“顶层”模块提供系统特定的实现

仅为示例:在库项目中,我有ActLogin活动,其中包含字段@Inject @Named("app version") mAppVersion。登录服务器时使用此字段的值。 ActLogin被几个使用此库的应用程序使用。每个应用的模块都有complete = true,并通过@Provides @Named("app version") provideAppVersion()

提供价值

Dagger 2(http://google.github.io/dagger/dagger-1-migration.html)迁移的文档说明:

  

Dagger 2模块全部声明为完整=假和库=真

同时“主要”文档页面(http://google.github.io/dagger/)声明:

  

Dagger注释处理器是严格的,如果任何绑定无效或不完整,将导致编译器错误。

后者显然是正确的,因为在尝试使用不满意的注入错误时会产生(error: java.lang.String cannot be provided without an @Provides- or @Produces-annotated method)。

问题是:是否可以将此方法(推迟提供注入)迁移到Dagger 2以及如何进行?

P.S。最初我认为这是一个肮脏的解决方法,在库的@Module中提供一些虚拟值,但是又一次 - 你不能在Dagger 2中使用模块覆盖(这是一种WTF(!!!)。模块覆盖对我来说是最有用的功能在创建单元测试时)。可能我错过了一些非常基本的东西,我希望有人可以指出: - )。

1 个答案:

答案 0 :(得分:10)

事实证明,有专门的构造,但需要一些时间才能找到它。 如果您需要一个具有包含未满足注入的模块的组件 - 请将其设为@Subcomponent。正如文件明确指出:

  

该关系允许子组件实现在声明时从其父组件继承整个绑定图。出于这个原因,子组件在完成之前不会被评估,直到它与父级相关联

所以在我的情况下,我的图书馆项目需要是一个匕首子组件。当我在我的app项目中使用它时,我的app dagger组件必须包含lib子组件。

在代码中:

库子组件:

@Subcomponent(modules = Mod1.class)
public interface MyLibraryComponent {
    void inject(Mod1Interface1 in);
}

应用程序组件:

@Component(modules = Mod2.class)
@Singleton
public interface MyAppComponent {
    void inject(MainActivity act);
    MyLibraryComponent newMyLibraryComponent();
}

请注意MyLibraryComponent newMyLibraryComponent(); - 这就是告诉匕首您的组件包含该子组件的方法。

图形实例化:

    MyAppComponent comp = DaggerMyAppComponent.builder().build();

请注意,与使用dependencies(@ Component' s属性)的组件构成相反,您不必手动"构建你的子组件。该组件将自动"如果子组件的模块不需要特殊配置(即构造函数参数),请注意这一点。如果某个子组件的模块需要配置,您可以通过组件实例化这样做:

MyAppComponent comp = DaggerMyAppComponent.builder().
                          mod2(new Mod2SpecialConfiguration()).
                          build();

对于Android,如果您的图书馆项目包含活动,则会有一个特殊的转折,因为每个活动都必须单独注入"按需提供"与普通的java应用程序相反,你通常在启动时注入整个应用程序。

为了举例,我们说我们的图书馆项目包含登录活动" ActLogin"我们在几个应用程序中使用它们。

@Subcomponent(modules = Mod1.class)
public interface MyLibraryComponent {
    void injectActLogin(ActLogin act);
    void inject(Mod1Interface1 in);
}

问题是在Android中我们通常在Application对象中创建依赖图,如下所示:

public class MyApplication extends Application {
    private MyAppComponent mAppDependencyInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        mAppDependencyInjector = DaggerMyAppComponent.builder().build();
    }

    public MyAppComponent getAppDependencyInjector() {
        return mAppDependencyInjector;
    }
}

然后在您的活动中,您可以像这样使用它:

@Override
protected void onCreate(Bundle savedInstanceState) {
    // ...
    ((MyApplication) getApplication()).getAppDependencyInjector().inject(this);
    // ...
}

但是我们的ActLogin活动是库项目(和dagger组件)的一部分,它不知道它将使用什么应用程序,因此我们将如何注入它?

有一个很好的解决方案,但请注意我不确定它是规范的(即文档中没有提到它,并不是作为一个例子由"权威& #34;(afaik))

Project's source can be found at github

首先,您必须在app app组件中扩展library dagger组件:

public interface MyAppComponent extends MyLibraryComponent {

这样您的应用程序组件将包含子组件中的所有inject方法,因此您也可以注入它的活动。毕竟,顶级组件实际上是整个对象图(更准确地说是Dagger生成的DaggerMyAppComponent代表整个图形),因此它能够在所有子组件中注入自身+所定义的所有内容。

现在我们必须确保图书馆项目能够访问它。我们创建了一个帮助类:

public class MyLibDependencyInjectionHelper {
    public static MyLibraryComponent getMyLibraryComponent(Application app) {
        if (app instanceof MyLibraryComponentProvider) {
            return ((MyLibraryComponentProvider) app).getMyLibraryComponent();
        } else {
            throw new IllegalStateException("The Application is not implementing MyLibDependencyInjectionHelper.MyLibraryComponentProvider");
        }
    }


    public interface MyLibraryComponentProvider {
        MyLibraryComponent getMyLibraryComponent();
    }
}

然后我们必须在MyLibraryComponentProvider课程中实施Application

public class MyApplication extends Application implements
    MyLibDependencyInjectionHelper.MyLibraryComponentProvider {
    // ...

    @Override
    public MyLibraryComponent getMyLibraryComponent() {
        return (MyLibraryComponent) mAppDependencyInjector;
    }
}

在ActLogin我们注入:

public class ActLogin extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
        // ...
        MyLibDependencyInjectionHelper.getMyLibraryComponent(getApplication()).
                           injectActLogin(this);
        // ...
    }
}

此解决方案存在问题:如果您忘记在应用程序中实现MyLibraryComponentProvider,则在编译时不会出现错误,但在启动ActLogin活动时会在运行时出错。幸运的是,通过简单的单元测试可以轻松避免这种情况。