我有一个由 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/