我正在尝试将Dagger依赖注入实现到我的应用程序中,但我很难理解它是如何工作的,特别是来自Spring,其中DI更容易且更具说明性。
我想要做的是拥有一堆可以在我的应用程序中使用的注入就绪对象,即SharedPreferences,网络对象(OkHttp,Retrofit,Picasso ......),以及EventBus和SchedulerProvider对象对于RxJava。
This sample似乎提供了我需要的一切,但我无法掌握一些概念。
在上一页中引用的other sample中,他们创建了一个使用NetModule中提供的Retrofit对象的GithubService。为此,他们创建了一个像这样的GithubComponent:
@UserScope
@Component(dependencies = NetComponent.class, modules = GitHubModule.class)
public interface GitHubComponent {
void inject(MainActivity activity);
}
他们正在使用UserScope注释来定义自己的范围。既然不能使用@Singleton,这是否意味着该对象不会是Singleton?范围如何真正影响DI?似乎他们只宣布一个命名范围没有更多的影响,但我不确定。
此外,我的应用程序是使用带有碎片的活动构建的。我是否必须为我的应用中的每个片段创建一个组件?即我需要在整个应用程序中使用我的REST api服务,我是否必须使用它们为每个屏幕声明一个组件?这会增加所需的样板代码量,因此听起来不太干净。
答案 0 :(得分:8)
组件应该是"大DI提供商"它提供了特定范围的所有内容。
例如,您可以拥有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
}
在模块中,您可以指定工厂方法("提供程序方法"),指定如何创建特定类型的依赖项。
例如,
@Module
public class NetworkingModule {
@Provides
@Singleton
OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()./*...*/.build();
}
@Provides
@Singleton
Retrofit retrofit(OkHttpClient okHttpClient) {
// ...
}
}
你可以把@Singleton
范围想象成Spring会给你的大容器。
您还可以使用@Inject
带注释的构造函数提供类的实例。这可以从组件中接收任何类,该类能够从该范围组件模块中的提供者方法实例化它(当然还有未编译的依赖项)。
@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,您还可以另外创建"子范围组件",它继承了从给定范围的组件提供的所有依赖关系。
例如,您可以继承所有@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
带注释的构造函数的类@Inject
带注释的字段@Component
提供方法@Inject
带注释的构造函数创建的类)手动现场注入可能发生在您自己不能创建的MainActivity
类。
手动字段注入仅注入您要注入的特定类。基类不会自动注入,他们需要在组件上调用.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);
}
}
答案 1 :(得分:0)
1)
由于不能使用@Singleton,这是否意味着对象会 不是单身人士?
GitHubComponent组件具有@UserScope范围,如果要在此范围内声明单例,则它应该是@Singleton。
2)
范围如何真正影响DI?看起来他们只是声明了一个 命名范围没有更多的影响,但我不确定。
范围注释适用于包含注射剂的类 构造函数并控制注入器如何重用该类型的实例。 默认情况下,如果不存在范围注释,则注入器会创建一个 实例(通过注入类型的构造函数),使用实例 一次注射,然后忘记它。如果存在范围注释, 注射器可以保留实例以便稍后重复使用 注射。
3)
我是否必须为我的应用中的每个片段创建一个组件?
您可以创建一次,实例化并存储在Application对象中,然后在每次需要注入片段或活动时进行查询。