android - 如何将带有 complete = false 的模块中缺少的注入(inject)从 Dagger 1 迁移到 Dagger 2

标签 android dependency-injection dagger dagger-2

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

仅作为示例:在库项目中,我有 ActLogin Activity ,该 Activity 具有字段 @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 modules are all declared as complete = false and library = true

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

The Dagger annotation processor is strict and will cause a compiler error if any bindings are invalid or incomplete.

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

问题是:是否可以将这种方法(延迟提供注入(inject))迁移到 Dagger 2 以及如何迁移?

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

最佳答案

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

That relationship allows the subcomponent implementation to inherit the entire binding graph from its parent when it is declared. For that reason, a subcomponent isn't evaluated for completeness until it is associated with a parent

所以在我的例子中,我的库项目需要是一个 Dagger 子组件。当我在我的应用程序项目中使用它时,我的应用程序 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(); - 这就是您告诉 Dagger 您的组件包含该子组件的方式。

图实例化:

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

请注意,在这种情况下,与使用具有 dependencies(@Component 的属性)的组件组合相反,您不必“手动”构建子组件。如果子组件的模块不需要特殊配置(即构造函数参数),组件将“自动”处理。如果某些子组件的模块需要配置,您可以通过组件实例化来完成,如下所示:

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

对于 android,如果您的库项目包含 Activity ,则有一个特殊的问题,因为每个 Activity 都必须“按需”单独注入(inject),这与通常在启动时注入(inject)整个应用程序的常规 java 应用程序相反。

为了举例,假设我们的图书馆项目包含登录 Activity “ActLogin”,我们将其作为多个应用程序的通用登录 Activity 。

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

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

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

但我们的 ActLogin Activity 是库项目(和 dagger 组件)的一部分,它不知道它将在哪个应用程序中使用,所以我们如何注入(inject)它?

有一个很好的解决方案,但请注意,我不确定它是否规范(即文档中未提及,“权威”未将其作为示例给出(afaik ))

Project's source can be found at github .

首先,您必须在您的应用程序组件中扩展库 Dagger 组件:

public interface MyAppComponent extends MyLibraryComponent {

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

现在我们必须确保库项目能够访问它。我们创建一个辅助类:

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

然后我们必须在我们的 Application 类中实现 MyLibraryComponentProvider:

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

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

在 ActLogin 中我们注入(inject):

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

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

关于android - 如何将带有 complete = false 的模块中缺少的注入(inject)从 Dagger 1 迁移到 Dagger 2,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31354735/

相关文章:

java - Struts 2 和 Spring 如何处理 struts 创建的对象

dependency-injection - InvalidOperationException : A circular dependency was detected for the service of type 'Microsoft. AspNetCore.Identity.UserManager

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

Android 系统跟踪错误号 8

android - Room API - 如何检索最近插入的实体生成 id?

java - 除了构造函数 @Inject 注解之外,还有其他方式实现 GWTP placeManager

android - android.content.Context.getString 中的 NPE 导致应用程序在启动时崩溃

android - 如何使用 Dagger 2.11 注入(inject)模拟

android - 按下主页按钮并重新打开应用程序后 MediaPlayer 出现 NullPointerException

android - 如何找出编译 'com.android.support:design:23.1.1'