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

Ogn*_*yan 3 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模块都声明为complete = false和library = true

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

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

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

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

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

Ogn*_*yan 10

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

该关系允许子组件实现在声明时从其父组件继承整个绑定图.因此,在与父组件关联之前,不会评估子组件的完整性

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

在代码中:

库子组件:

@Subcomponent(modules = Mod1.class)
public interface MyLibraryComponent {
    void inject(Mod1Interface1 in);
}
Run Code Online (Sandbox Code Playgroud)

应用程序组件:

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

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

图形实例化:

    MyAppComponent comp = DaggerMyAppComponent.builder().build();
Run Code Online (Sandbox Code Playgroud)

请注意,与dependencies在这种情况下使用具有(@ Component的属性)的组件组合相反,您不必"手动"构造子组件.如果子组件的模块不需要特殊配置(即构造函数参数),组件将"自动"注意这一点.如果某个子组件的模块需要配置,则通过组件实例化执行此操作,如下所示:

MyAppComponent comp = DaggerMyAppComponent.builder().
                          mod2(new Mod2SpecialConfiguration()).
                          build();
Run Code Online (Sandbox Code Playgroud)

对于android,如果您的库项目包含活动,则会有一个特殊的变化,因为每个活动都必须"按需"单独注入,这与常规Java应用程序相反,在常规Java应用程序中,您通常在启动时注入整个应用程序一次.

为了举例说明,我们的库项目包含登录活动"ActLogin",我们将其用作多个应用程序的通用.

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

问题是在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在你的活动中你使用它像这样:

@Override
protected void onCreate(Bundle savedInstanceState) {
    // ...
    ((MyApplication) getApplication()).getAppDependencyInjector().inject(this);
    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

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

Project的源代码可以在github上找到.

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

public interface MyAppComponent extends MyLibraryComponent {
Run Code Online (Sandbox Code Playgroud)

这样您的应用程序组件将包含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();
    }
}
Run Code Online (Sandbox Code Playgroud)

那么我们必须MyLibraryComponentProvider在我们Application班上实现:

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

    @Override
    public MyLibraryComponent getMyLibraryComponent() {
        return (MyLibraryComponent) mAppDependencyInjector;
    }
}
Run Code Online (Sandbox Code Playgroud)

在ActLogin我们注入:

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

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