文档类型:翻译
原文链接 : https://android.jlelse.eu/android-and-dagger-2-10-androidinjector-5e9c523679a3
译者注:转载请注明原文及译文出处,本文仅供学术交流,不作任何商业使用,如有侵权,请联系删除。
Dagger版本:2.16
Dagger 2.10版本引入的dagger-android是一个专为Android设计的除了Dagger主模块和dagger-compiler之外的全新的模块,本文中,我们将介绍使用dagger-android的方法步骤,当然,前提是你要具备相关的Dagger知识。
本文主要介绍Activity的注入,但是也可以作为其他Android组件注入的参考
Dagger常用配置
在Android端的Dagger的常用配置包含包含Application Component
以及Application Module
,前者是用来注入Activity,Fragment等Android组件。
Application Component
代码
@Component(modules = { AppModule.class })
interface AppComponent {
@Component.Builder
interface Builder {
@BindsInstance Builder application(App application);
AppComponent build();
}
void inject(FeatureActivity featureActivity);
}
Application Module
代码
@Module
abstract class AppModule {
@Provides
static Context provideContext(App application) {
return application.getApplicationContext();
}
@Singleton
@Provides
static SomeClientApi provideSomeClientApi() {
return new SomeClientApiImpl();
}
}
App的Application
代码
@Module
abstract class AppModule {
@Provides
static Context provideContext(App application) {
return application.getApplicationContext();
}
@Singleton
@Provides
static SomeClientApi provideSomeClientApi() {
return new SomeClientApiImpl();
}
}
因为Android框架已经将这些组件实例化,所以我们必须执行成员注入,使用 @Inject
注解对可见类进行标注,如下:
public class FeatureActivity extends AppCompatActivity {
@Inject SomeClientApi mSomeClientApi;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((App)getApplication())
.getAppComponent()
.inject(this);
}
}
然而,这种方式破坏了依赖注入的核心准则:一个类不应该知道它是如何被注入的
,最新引入的dagger-android模块就是为了解决这个问题——将注入类与注入器分离开来。
使用新的dagger-android模块
首先,将以下Gradle的依赖添加到你的build.gradle
文件中:
// Dagger core dependencies
annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
implementation 'com.google.dagger:dagger:2.16'
// Dagger Android dependencies
annotationProcessor 'com.google.dagger:dagger-android-processor:2.16'
implementation 'com.google.dagger:dagger-android:2.16'
implementation 'com.google.dagger:dagger-android-support:2.16'
在我们的项目中,新模块的配置需要不止一个component和module。每一个Activity都需要自己的subComponent并将其与Application的Component连接起来。以下是我建议的类结构:
/
| App (extending Application)
| AppComponent
| AppModule
| ContributeAndroidInjectorsModule // 3
+ feature/
| FeatureModule // 2
| FeatureActivityModule // 1
| FeatureActivity
正如我们所见,在上文中所提到的典型配置之外额外增加了三个类。每一个feature拥有它自己的component以及module。
注:我使用术语“feature”来描述一个app中的显示界面(或者说一个Activity)。
1.FeatureActivityModule
正如前文提到的,每一个Activity现在需要它自己的subcomponent,为此,Dagger引入了一个非常方便的注解来供我们使用以提醒Dagger生成subcomponent的代码。就像这样:
@Module
public abstract class FeatureActivityModule {
@ActivityScope
@ContributesAndroidInjector(modules = { FeatureModule.class })
abstract FeatureActivity contributeFeatureActivityInjector();
}
2.FeatureModule
我们将Activity自身需要的绑配置在这个module中。此module中的绑定仅仅在此Activity和它的subcomponent中有效,除非该module在其他component中使用。假设我们正在使用MVP的设计模式,一下是你通常用来绑定View的方式:
@Module
abstract class FeatureModule {
@ActivityScope
@Binds
abstract FeatureView provideFeatureView(FeatureActivity featureActivity);
}
3.ContributeActivityModule
到目前为止,我们告诉了Dagger为我们的Activity生成subcomponent,但是我们并没有将subcomponent与Applicaiton的Component连接起来。为了实现这个,我们需要另一个module来持有所有Activity的module。这个module将会被包含在Application的Component中。
@Module(includes = {
FeatureActivityModule.class
// Other Activity modules will be added here
})
abstract class ContributeActivityModule {
}
4.AppComponent
Applicaiton的Component配置(使用Kotlin)
我更喜欢使用Java来写Dagger的module,因为Kotlin缺少static修饰符,所以当我们使用companion object时需要在同一个类中增加额外的
@Module注解
,这是我不太喜欢的。
@Singleton
@Component(modules = [
AndroidSupportInjectionModule::class, // 1
ContributeActivityModule::class, // 2
AppModule::class
])
interface AppComponent : AndroidInjector { // 3
@Component.Builder
abstract class Builder : AndroidInjector.Builder() // 4
}
(译者注:Java代码如下)
@Singleton
@Component(modules = [
AndroidSupportInjectionModule::class, // 1
ContributeActivityModule::class, // 2
AppModule::class
])
public interface AppComponent extends AndroidInjector { // 3
@Component.Builder
abstract class Builder extends AndroidInjector.Builder {
}
}
注意:AppComponent
中的一些修改:
- 为了使Dagger Android能够正常运行,我们在Application的Component中包含了AndroidSupportInjectionModule
- 包含ContributeActivityModule是为了将Activity的subcomponent与AppComponent连接起来
- AppComponent必须继承自AndroidInjector 并将其泛型设定为
Application
类 - Appcomponent的Builder可以选择性的继承AndroidInjector.Builder并提供Application类。因为这个base类已经实现了公共的component builder,这个builder中包含了一个设置Application实例的方法以及另一个构建component的方法,这可以很轻松的为我们介绍一些代码行数。
5.然后我们修改我们的App
文件,使其继承DaggerApplication
并实现基本方法,代码如下
class App : DaggerApplication() {
private val appComponent: AndroidInjector by lazy {
DaggerAppComponent
.builder()
.create(this)
}
override fun applicationInjector(): AndroidInjector {
return appComponent
}
}
(译者注:Java代码如下)
public class App extends DaggerApplication {
private AppComponent appCompoent;
@Override
public void onCreate() {
appCompoent = DaggerAppComponent.builder()
.application(this)
.build();
super.onCreate();
}
@Override
protected AndroidInjector extends DaggerApplication> applicationInjector() {
return appCompoent;
}
}
6.最后,在FeatureActivity
中,我们将之前写的注入Activity的代码删除,同时使其继承自DaggerAppCompatActivity
而不是AppCompatActivity
class FeatureActivity : DaggerAppCompatActivity(), FeatureView {
@Inject
internal lateinit var presenter: FeaturePresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.feature_activity)
}
}
(译者注:Java代码如下)
public class FeatureActivity extends DaggerAppCompatActivity implements FeatureView {
public static final String EXTRA_SOME_ID = "some_id";
@Inject FeaturePresenter presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.feature_activity);
}
}
向Acivity的Component中注入自定义参数
在某些场景下你希望注入某些由Activity自己提供的参数。之前常用的方法是在调用Activity的component的inject()
法法之前,将参数传递给module的构造函数。例如,如果我们遵循MVP的设计模式,我们饿Presenter将会把View作为其构造参数的一部分,这意味着我们需要将Activity作为一个参数传递给module的构造函数。在使用dagger-android之前,写法是这样:
@Module
class FeatureModule {
private FeatureView view;
public FeatureModule(FeatureView view) {
this.view = view;
}
@Provides FeatureView provideView() {
return view;
}
}
同时,在Presenter中我们会使用构造函数注入:
class FeaturePresenter {
private final FeatureView view;
@Inject
Presenter(FeatureView view) {
this.view = view;
}
public void doSomething() {
}
}
最后,构建Component,传入一个mudle的新实例,并注入Activity:
class FeaturePresenter {
private final FeatureView view;
@Inject
Presenter(FeatureView view) {
this.view = view;
}
public void doSomething() {
}
}
但是,我们如何使用新的dagger-android模块来实现这个呢?毕竟,我们不再需要手动注入Activity,因此无法像以前那样接触到module的创建。
答案是,我们不再需要这样做了。使用dagger-android模块,Activity已经是图表的一部分了。这到底是什么意思呢?你应该可以记得,我们在FeatureModule
做了一下配置,用于在任何使用FeatureView
的地方,绑定使用FeatureActivity
:
@Module
public abstract class FeatureModule {
@Binds
abstract FeatureView provideFeatureView(FeatureActivity featureActivity);
}
}
但是,假如我们想通过接受Activity的Intent向Presenter传递参数呢?假设你通过Activity的Intent的extra传递了一个唯一的ID,并且Presenter需要这个ID,例如,Presenter需要这个ID来进行HTTP请求,实现这个的方式就是使用@Named
注解作为限定符,因此我们的Presenter的代码如下:
class FeaturePresenter {
private FeatureView featureView;
private String someId;
@Inject
public FeaturePresenter(FeatureView featureView, @Named("someId") String someId) {
this.featureView = featureView;
this.someId = someId;
}
public void foo() {
featureView.showFoo();
}
}
答案是非常简单的,我们给FretureModule增加了一个Provides方法,它将会持有一个FeatureActivity的实例,并从intent的extras中获取我们需要的信息:
@Module
abstract class FeatureModule {
@ActivityScope
@Binds
abstract FeatureView provideFeatureView(FeatureActivity featureActivity);
@ActivityScope
@Provides
@Named("someId")
static String provideSomeId(FeatureActivity featureActivity) {
return featureActivity.getIntent().getStringExtra(FeatureActivity.EXTRA_SOME_ID);
}
}
如前所述,这因为FeatureActivity
已经在图标中,所以这种实现成为可能。
除了Activity之外的注入
如本文开头所提到的,Fragment注入和其他Android组件都不在本文的范围之内。官方文档介绍了注入Fragment对象,我强烈建议您阅读它。还有更多关于Services、Receivers 和ContentProviders的信息。
总结
Dagger-android模块非常接近Android的依赖注入。如果你正在开发一个新的项目并且准备使用Dagger,你一定要使用Dagger-Android的配置。
官方的示例对我来说太简单了,所以你可以看看我自己的Demo。