如何将带有 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 时会在运行时收到错误。幸运的是,通过简单的单元测试可以轻松避免这种情况。
我有一个库 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 时会在运行时收到错误。幸运的是,通过简单的单元测试可以轻松避免这种情况。