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

How to migrate missing inject from module with complete = false from Dagger 1 to Dagger 2

我有一个库 project/module,Android 应用程序和常规 java 应用程序都使用它。 在 Dagger 1 中,这个 project/module 有 属性 complete = false。其中有一个 @Inject 字段,任何 class 实现或 @Provides 方法都不满足。这个想法是强制具有 complete = true 的 "top" 模块提供系统特定的实现

仅举个例子:在图书馆项目中,我有 ActLogin 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

同时 "main" 文档页面 (http://google.github.io/dagger/) 指出:

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

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

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

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

事实证明,有专门的结构用于此,但需要一些时间才能找到它。 如果您需要一个包含未满足注入的模块的组件 - 将其设为@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

所以在我的例子中,我的库项目需要是一个匕首子组件。当我在我的应用程序项目中使用它时,我的应用程序匕首组件必须包含 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 的 属性)相反,在这种情况下,您不必 "manually" 构造您的子组件。如果子组件的模块不需要特殊配置(即构造函数参数),组件将 "automatically" 负责。如果某些子组件的模块需要配置,您可以通过这样的组件实例化来进行配置:

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

对于 android 如果您的图书馆项目包含活动,则有一个特殊的转折,因为每个 activity 都必须单独注入 "on demand" 与您在其中的常规 java 应用程序相反通常在启动时注入整个应用程序。

举个例子,假设我们的库项目包含登录名 activity "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;
    }
}

然后在你的 activity 中你可以这样使用它:

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

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

有一个很好的解决方案,但 请注意,我不确定它是规范的(即它没有在文档中提及,它没有作为示例给出"authorities" (afaik))

Project's source can be found at github.

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

public interface MyAppComponent extends MyLibraryComponent {

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

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

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 class:

中实现 MyLibraryComponentProvider
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 activity 时会在运行时收到错误。幸运的是,通过简单的单元测试可以轻松避免这种情况。