Android上常见的Dagger设置通常涉及Application Component和Application Module,其中前者用于注入组件(例如Activity,Fragment等)。
@Component(modules = { AppModule.class })
public interface AppComponent {
@Component.Builder
interface Builder {
@BindsInstance Builder application(App application);
AppComponent build();
}
void inject(FeatureActivity featureActivity);
}
@Module
public class AppModule {
@Provides Context provideContext(App application) {
return application.getApplicationContext();
}
@Singleton @Provides SomeClientApi provideSomeClientApi() {
return new SomeClientApiImpl();
}
}
public class App extends Application {
private AppComponent appComponent;
@Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent
.builder()
.application(this)
.build();
}
public AppComponent getAppComponent() {
return appComponent;
}
}
由于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.10'
compile 'com.google.dagger:dagger:2.10'
// Dagger Android dependencies
annotationProcessor 'com.google.dagger:dagger-android-processor:2.10'
compile 'com.google.dagger:dagger-android:2.10'
// Use this instead if you're using support library
compile 'com.google.dagger:dagger-android-support:2.10'
如果您从旧版本的Dagger进行升级,请确保将版本(2.10)设置为所有与匕首相关的依赖关系。另外值得一提的是,我在本教程中使用Android Gradle插件2.3.0。如果您使用的版本比2.2.0老,你要替换annotationProcessor用apt。在我们的项目中,新模块的设置不仅仅需要一个组件和一个模块。我的建议是具有以下类结构:
/
| App (extending Application)
| AppComponent
| AppModule
| BuildersModule
- feature/
|FeatureModule
|FeatureSubComponent
我们可以看到,在上面的典型设置之上,我们还添加了3个类(BuildersModule,FeatureModule,FeatureSubComponent)。每个功能都有自己的sub component和module。
我使用术语“ Feature”来描述应用程序中的一个屏幕(或Activity)。
1.subcomponent必须继承AndroidInjector<TheActivity>并具有继承AndroidInjector.Builder<TheActivity>的抽象类
@Subcomponent
public interface FeatureSubComponent extends AndroidInjector<FeatureActivity> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<FeatureActivity> {
}
}
2.然后,我们需要在BuildersModule中添加绑定到sub component的Builder中,以便Dagger能够注入FeatureActivity。后期的 sub components builders的绑定也需要添加到此类中。
@Module
public abstract class BuildersModule {
@Binds
@IntoMap
@ActivityKey(FeatureActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindFeatureActivityInjectorFactory(FeatureActivitySubComponent.Builder builder);
// Add more bindings here for other sub components
}
在此为其他子组件添加更多绑定
3.接下来,我们需要连接FeatureSubComponent,AppModule通过在@Module注解里面指定subcomponents
@Module(subcomponents = { FeatureSubComponent.class })
public class AppModule {
@Provides Context provideContext(App application) {
return application.getApplicationContext();
}
@Singleton @Provides SomeClientApi provideSomeClientApi() {
return new SomeClientApiImpl();
}
}
4.和电线
BuildersModule到AppComponent:
@Component(modules = {
/* Use AndroidInjectionModule.class if you're not using support library */
AndroidSupportInjectionModule.class,
AppModule.class,
BuildersModule.class })
public interface AppComponent {
@Component.Builder
interface Builder {
@BindsInstance Builder application(App application);
AppComponent build();
}
void inject(App app);
}
请注意AppComponent:除了添加BuildersModule的@Component(modules= {…}),我们还增加了AndroidSupportInjectionModule,这是一个内置的模块匕首,机器人必须安装在AppComponent按照该规定的官方文档,这是必要的,以确保所有绑定必要对这些基站类型可用。
我们删除了inject()方法,FeatureActivity并用inject()我们的App类的方法替换了它。
5.然后,我们修改我们的App类,以便它实现HasDispatchingActivityInjector和@Inject一个DispatchingAndroidInjector<Activity>从返回activityInjector()的方法。在onCreate(),我们建立AppComponent并且调用inject(this)。
public class App extends Application implements HasDispatchingActivityInjector {
@Inject DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent
.builder()
.application(this)
.build()
.inject(this);
}
@Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
}
请注意,在Dagger 2.11版中,所有HasDispatchingInjector的界面都已被重命名为HasInjectors
最后,FeatureActivity我们删除了我们在那里注入活动的代码,而我们在调用之前调用,因为调用超级在配置更改期间附加上一个活动实例的Fragments,而后者又会注入Fragments。为了使Fragment注入成功,必须注意Activity:AndroidInjection.inject(this) super.onCreate()
public class FeatureActivity extends AppCompatActivity implements FeatureView {
@Inject SomeClientApi mSomeClientApi;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
向Activity自己的组件注入自定义参数
在某些情况下,您需要注入活动本身提供的参数。之前完成的常见方法是在调用inject()活动的组件之前将参数传递给模块的构造函数。例如,如果我们按照这里描述的MVP设计模式,我们的Presenter将把View作为构造函数参数的一部分。这意味着我们需要将该活动作为参数传递给模块构造函数。在匕首android之前,这样做是这样做的:
@Module
class FeatureModule {
private FeatureView view;
public FeatureModule(FeatureView view) {
this.view = view;
}
@Provides FeatureView provideView() {
return view;
}
}
在演示者中,我们将使用构造函数注入:
class FeaturePresenter {
private final FeatureView view;
@Inject
Presenter(FeatureView view) {
this.view = view;
}
public void doSomething() {
}
}
最后,构建组件,传递一个新的模块实例并注入该活动:
public class FeatureActivity extends AppCompatActivity implements FeatureView {
@Inject FeaturePresenter presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerFeatureComponent.builder()
.featureModule(FeatureModule(this)).build()
.inject(this)
// presenter ready to be used
presenter.doNothing();
}
}
但是我们如何用新的匕首 - Android模块呢?毕竟,我们在活动中只有一个调用 - 没有办法像以前那样传递一个新的模块实例。
AndroidInjection.inject(this)
答案是,我们不再需要这样做了。使用dagger-android模块,该活动已经是图表的一部分。那实际上是什么意思呢?这意味着我们所有的都创建一个绑定,将在任何FeatureView请求的地方注入活动。这样做的方法是使用Dagger的@Binds注释。为此,我们将创建一个新的模块类FeatureModule,它将包含所有特性绑定。
@Module
public abstract class FeatureModule {
@Binds
abstract FeatureView provideFeatureView(FeatureActivity featureActivity);
}
}
这是伟大的,但你可能想知道,如果我想传递除活动之外的其他参数呢?假设您有一个唯一的ID,通过活动的附加功能传递给活动,演示者需要它。例如,假设演示者需要此ID才能发出HTTP请求。执行此操作的方法是使用带有@Named注释的限定符。所以我们的主持人会像这样:
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 doNothing() {
featureView.doNothing();
}
}
现在我们已经看到,通过AndroidInjection.inject(this)调用注入活动是不可能传递参数的。那么我们如何使图形知道这个ID?这有点棘手,可能实际上是一个黑客,但目前一种方法是将ID保存在活动中的一个字段中,然后FeatureModule提供它。
1.首先我们将ID保存在一个字段中 FeatureActivity
public class FeatureActivity extends AppCompatActivity implements FeatureView {
@Inject FeaturePresenter presenter;
String someId;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
someId = getIntent().getStringExtra(EXTRA_SOME_ID);
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
presenter.doNothing();
}
@Override
public void doNothing() {
}
}
然后我们添加一个提供方法 FeatureModule
@Module
public abstract class FeatureModule {
@Binds
abstract FeatureView provideFeatureView(FeatureActivity featureActivity);
@Provides @Named("someId") static String provideSomeId(FeatureActivity featureActivity) {
return featureActivity.someId;
}
}
如前所述,这是可能的,因为FeatureActivity已经在图中。
注入活动以外
如本教程开头所述,Fragment注入以及其他Android组件不在本教程的范围之内。在官方文件涵盖了片段下喷吹片段的对象,我强烈建议你阅读它。还有更多关于服务和接收器的信息。
结论
新的dagger-android模块正在使我们更接近于适当的依赖注入,尽管在我看来,它比以前更复杂。
匕首团队正在积极致力于进一步简化,我不用怀疑API将尽快进行其他更改。
在撰写本教程之前,还没有提供官方样品。
您可能想要查看
原文链接
[https://android.jlelse.eu/android-and-dagger-2-10-androidinjector-5e9c523679a3]