Dagger2 和 Android
Dagger2 and Android
我正在尝试在我的应用程序中实施 Dagger 依赖注入,但我很难理解它是如何工作的,尤其是来自 Spring DI 更容易和更具声明性的地方。
我想要做的是拥有一堆可以在我的应用程序中使用的准备好注入的对象,即 SharedPreferences、网络对象(OkHttp、Retrofit、Picasso...)、EventBus 和一个RxJava 的 SchedulerProvider 对象。
This sample 似乎提供了我需要的一切,但我无法理解一些概念。
在上一页引用的 other sample 中,他们创建了一个 GithubService,它使用 NetModule 中提供的 Retrofit 对象。为此,他们创建了一个像这样的 GithubComponent:
@UserScope
@Component(dependencies = NetComponent.class, modules = GitHubModule.class)
public interface GitHubComponent {
void inject(MainActivity activity);
}
他们正在使用定义自己范围的 UserScope 注释。既然不能使用@Singleton,是不是意味着这个对象就不会是Singleton了呢?范围如何真正影响 DI?他们似乎只是在声明一个命名范围,没有更多的作用,但我不确定。
此外,我的应用程序是使用 Activity with Fragments 构建的。我是否必须为应用程序中的每个片段创建一个组件?即我需要在整个应用程序中使用我的 REST api 服务,我是否必须为使用它们的每个屏幕声明一个组件?这增加了所需的样板代码数量,因此听起来不是很干净。
1)
Since @Singleton cannot be used, does this mean that the object will
not be a Singleton?
GitHubComponent 组件有@UserScope 范围,如果你想在这个范围内声明单例,它应该是@Singleton。
2)
How do scopes really affect the DI? It seems they are only declaring a
named-scope with no more effect, but I'm not sure.
范围注解适用于包含可注入的 class
构造函数并控制注入器如何重用该类型的实例。
默认情况下,如果不存在范围注解,注入器会创建一个
实例(通过注入类型的构造函数),将实例用于
一针,然后忘记了。如果存在范围注释,
注入器可能会保留实例以供以后可能重用
注射.
3)
Do I have to create a Component for every Fragment in my app?
您可以创建一次,实例化并存储在您的 Application 对象中,然后在您每次需要注入某些内容时查询它,如果您的片段或 activity。
组件应该是为特定范围提供一切的"big DI provider"。
例如,您可以有一个 SingletonComponent
和 @Singleton
作用域,其中添加的每个模块至少有一个 @Singleton
作用域提供程序方法。
@Singleton
@Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class})
public interface SingletonComponent {
// provision methods
OkHttpClient okHttpClient();
RealmHolder realmHolder();
// etc.
}
您可以为每个模块定义配置方法。
public interface DatabaseComponent {
RealmHolder realmHolder();
}
public interface NetworkingComponent{
OkHttpClient okHttpClient();
}
在这种情况下你会
@Singleton
@Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class})
public interface SingletonComponent
extends NetworkingComponent, DatabaseComponent, MapperComponent, UtilsComponent {
// provision methods inherited
}
在模块中,您可以指定工厂方法 ("provider method"),指定如何创建特定类型的依赖项。
例如,
@Module
public class NetworkingModule {
@Provides
@Singleton
OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()./*...*/.build();
}
@Provides
@Singleton
Retrofit retrofit(OkHttpClient okHttpClient) {
// ...
}
}
您可以将 @Singleton
作用域想象成 Spring 给您的大型 DI 容器。
您还可以使用 @Inject
带注释的构造函数提供 class 的实例。这可以从组件接收任何 class,该组件能够从该作用域组件模块(当然还有非作用域依赖项)中的提供程序方法实例化它。
@Singleton
public class MyMapper {
@Inject
public MyMapper(RealmHolder realmHolder, OkHttpClient okHttpClient) { // totally random constructor for demo
}
}
或
@Singleton
public class MyMapper {
@Inject
RealmHolder realmHolder;
@Inject
OkHttpClient okHttpClient;
@Inject
public MyMapper() {
}
}
那么这个在组件中是可用的,你甚至可以为它设置方法让它在组件依赖中可以被继承:
@Singleton
@Component(modules={...})
public interface SingletonComponent {
MyMapper myMapper();
}
使用 Dagger2,您还可以另外创建 "subscoped components",它继承从给定范围的组件提供的所有依赖项。
例如,您可以继承所有 @Singleton
范围内的组件,但您仍然可以在每个新范围内拥有新范围内的依赖项,例如 @ActivityScope
.
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}
然后,您可以使用子组件或组件依赖项创建子范围组件。
- 子组件:
.
@ActivityScope
@Subcomponent(modules={MainActivityModule.class})
public interface MainActivityComponent {
MainPresenter mainPresenter();
}
然后可以在其父作用域组件中创建:
@Singleton
@Component(modules={...})
public interface SingletonComponent {
MainActivityComponent mainActivityComponent(MainActivityModule module);
}
然后就可以使用单例组件来实例化这个了:
SingletonComponent singletonComponent = DaggerSingletonComponent.create();
MainActivityComponent mainActivityComponent = singletonComponent.mainActivityComponent(new MainActivityModule(mainActivityHolder));
- 组件依赖:
.
@ActivityScope
@Component(dependencies={SingletonComponent.class}, modules={MainActivityModule.class})
public interface MainActivityComponent extends SingletonComponent {
MainPresenter mainPresenter();
}
为此,您必须在超作用域组件中指定提供方法。
然后你可以像这样实例化它:
SingletonComponent singletonComponent = DaggerSingletonComponent.create();
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.singletonComponent(singletonComponent)
.mainActivityModule(new MainActivityModule(mainActivityHolder))
.build();
在 Dagger2 中,您可以通过以下方式获取依赖项:
@Inject
带注释的构造函数参数
@Inject
class 上带有 @Inject
注释构造函数 的注释字段
- 来自
@Component
提供方法
- 通过组件中定义的手动字段注入方法(对于 classes 你不能使用
@Inject
带注释的构造函数创建)
手动字段注入可以发生在 class 类 MainActivity
中,您自己不会创建。
手动字段注入仅注入您正在注入的特定 class。 Base-classes 不会自动注入,它们需要在组件上调用 .inject(this)
。
它是这样工作的:
@ActivityScope
@Subcomponent(modules={MainActivityModule.class})
public interface MainActivityComponent {
void inject(MainActivity mainActivity);
}
那么你可以这样做:
public class MainActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.singletonComponent(getSingletonComponent())
.mainActivityModule(new MainActivityModule(this))
.build(); // ensure activity `holder` instead, and retain component in retained fragment or `non-configuration instance`
mainActivityComponent.inject(this);
}
}
我正在尝试在我的应用程序中实施 Dagger 依赖注入,但我很难理解它是如何工作的,尤其是来自 Spring DI 更容易和更具声明性的地方。
我想要做的是拥有一堆可以在我的应用程序中使用的准备好注入的对象,即 SharedPreferences、网络对象(OkHttp、Retrofit、Picasso...)、EventBus 和一个RxJava 的 SchedulerProvider 对象。
This sample 似乎提供了我需要的一切,但我无法理解一些概念。
在上一页引用的 other sample 中,他们创建了一个 GithubService,它使用 NetModule 中提供的 Retrofit 对象。为此,他们创建了一个像这样的 GithubComponent:
@UserScope
@Component(dependencies = NetComponent.class, modules = GitHubModule.class)
public interface GitHubComponent {
void inject(MainActivity activity);
}
他们正在使用定义自己范围的 UserScope 注释。既然不能使用@Singleton,是不是意味着这个对象就不会是Singleton了呢?范围如何真正影响 DI?他们似乎只是在声明一个命名范围,没有更多的作用,但我不确定。
此外,我的应用程序是使用 Activity with Fragments 构建的。我是否必须为应用程序中的每个片段创建一个组件?即我需要在整个应用程序中使用我的 REST api 服务,我是否必须为使用它们的每个屏幕声明一个组件?这增加了所需的样板代码数量,因此听起来不是很干净。
1)
Since @Singleton cannot be used, does this mean that the object will not be a Singleton?
GitHubComponent 组件有@UserScope 范围,如果你想在这个范围内声明单例,它应该是@Singleton。
2)
How do scopes really affect the DI? It seems they are only declaring a named-scope with no more effect, but I'm not sure.
范围注解适用于包含可注入的 class 构造函数并控制注入器如何重用该类型的实例。 默认情况下,如果不存在范围注解,注入器会创建一个 实例(通过注入类型的构造函数),将实例用于 一针,然后忘记了。如果存在范围注释, 注入器可能会保留实例以供以后可能重用 注射.
3)
Do I have to create a Component for every Fragment in my app?
您可以创建一次,实例化并存储在您的 Application 对象中,然后在您每次需要注入某些内容时查询它,如果您的片段或 activity。
组件应该是为特定范围提供一切的"big DI provider"。
例如,您可以有一个 SingletonComponent
和 @Singleton
作用域,其中添加的每个模块至少有一个 @Singleton
作用域提供程序方法。
@Singleton
@Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class})
public interface SingletonComponent {
// provision methods
OkHttpClient okHttpClient();
RealmHolder realmHolder();
// etc.
}
您可以为每个模块定义配置方法。
public interface DatabaseComponent {
RealmHolder realmHolder();
}
public interface NetworkingComponent{
OkHttpClient okHttpClient();
}
在这种情况下你会
@Singleton
@Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class})
public interface SingletonComponent
extends NetworkingComponent, DatabaseComponent, MapperComponent, UtilsComponent {
// provision methods inherited
}
在模块中,您可以指定工厂方法 ("provider method"),指定如何创建特定类型的依赖项。
例如,
@Module
public class NetworkingModule {
@Provides
@Singleton
OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()./*...*/.build();
}
@Provides
@Singleton
Retrofit retrofit(OkHttpClient okHttpClient) {
// ...
}
}
您可以将 @Singleton
作用域想象成 Spring 给您的大型 DI 容器。
您还可以使用 @Inject
带注释的构造函数提供 class 的实例。这可以从组件接收任何 class,该组件能够从该作用域组件模块(当然还有非作用域依赖项)中的提供程序方法实例化它。
@Singleton
public class MyMapper {
@Inject
public MyMapper(RealmHolder realmHolder, OkHttpClient okHttpClient) { // totally random constructor for demo
}
}
或
@Singleton
public class MyMapper {
@Inject
RealmHolder realmHolder;
@Inject
OkHttpClient okHttpClient;
@Inject
public MyMapper() {
}
}
那么这个在组件中是可用的,你甚至可以为它设置方法让它在组件依赖中可以被继承:
@Singleton
@Component(modules={...})
public interface SingletonComponent {
MyMapper myMapper();
}
使用 Dagger2,您还可以另外创建 "subscoped components",它继承从给定范围的组件提供的所有依赖项。
例如,您可以继承所有 @Singleton
范围内的组件,但您仍然可以在每个新范围内拥有新范围内的依赖项,例如 @ActivityScope
.
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}
然后,您可以使用子组件或组件依赖项创建子范围组件。
- 子组件:
.
@ActivityScope
@Subcomponent(modules={MainActivityModule.class})
public interface MainActivityComponent {
MainPresenter mainPresenter();
}
然后可以在其父作用域组件中创建:
@Singleton
@Component(modules={...})
public interface SingletonComponent {
MainActivityComponent mainActivityComponent(MainActivityModule module);
}
然后就可以使用单例组件来实例化这个了:
SingletonComponent singletonComponent = DaggerSingletonComponent.create();
MainActivityComponent mainActivityComponent = singletonComponent.mainActivityComponent(new MainActivityModule(mainActivityHolder));
- 组件依赖:
.
@ActivityScope
@Component(dependencies={SingletonComponent.class}, modules={MainActivityModule.class})
public interface MainActivityComponent extends SingletonComponent {
MainPresenter mainPresenter();
}
为此,您必须在超作用域组件中指定提供方法。
然后你可以像这样实例化它:
SingletonComponent singletonComponent = DaggerSingletonComponent.create();
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.singletonComponent(singletonComponent)
.mainActivityModule(new MainActivityModule(mainActivityHolder))
.build();
在 Dagger2 中,您可以通过以下方式获取依赖项:
@Inject
带注释的构造函数参数@Inject
class 上带有@Inject
注释构造函数 的注释字段
- 来自
@Component
提供方法 - 通过组件中定义的手动字段注入方法(对于 classes 你不能使用
@Inject
带注释的构造函数创建)
手动字段注入可以发生在 class 类 MainActivity
中,您自己不会创建。
手动字段注入仅注入您正在注入的特定 class。 Base-classes 不会自动注入,它们需要在组件上调用 .inject(this)
。
它是这样工作的:
@ActivityScope
@Subcomponent(modules={MainActivityModule.class})
public interface MainActivityComponent {
void inject(MainActivity mainActivity);
}
那么你可以这样做:
public class MainActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.singletonComponent(getSingletonComponent())
.mainActivityModule(new MainActivityModule(this))
.build(); // ensure activity `holder` instead, and retain component in retained fragment or `non-configuration instance`
mainActivityComponent.inject(this);
}
}