文档类型:翻译
原文链接 : 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 代码

@Moduleabstract class AppModule {    @Provides     static Context provideContext(App application) {        return application.getApplicationContext();    }    @Singleton     @Provides     static SomeClientApi provideSomeClientApi() {        return new SomeClientApiImpl();    }}

App的Application 代码

@Moduleabstract 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 dependenciesannotationProcessor 'com.google.dagger:dagger-compiler:2.16'implementation 'com.google.dagger:dagger:2.16'// Dagger Android dependenciesannotationProcessor '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的代码。就像这样:

@Modulepublic abstract class FeatureActivityModule {    @ActivityScope    @ContributesAndroidInjector(modules = { FeatureModule.class })    abstract FeatureActivity contributeFeatureActivityInjector();}

2.FeatureModule

我们将Activity自身需要的绑配置在这个module中。此module中的绑定仅仅在此Activity和它的subcomponent中有效,除非该module在其他component中使用。假设我们正在使用MVP的设计模式,一下是你通常用来绑定View的方式:

@Moduleabstract 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<App> { // 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之前,写法是这样:

@Moduleclass 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

@Modulepublic 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中获取我们需要的信息:

@Moduleabstract 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。

更多相关文章

  1. Android jni中数组参数的传递方式
  2. Android录音时获取分贝值的方法代码实例
  3. android 手机管理软件 发布开源代码
  4. 实用代码3
  5. Android 访问Webservice接口,参数对象不能串行化问题解决(java.lan
  6. Android中main.xml界面参数笔记
  7. Android使用代码实现关机/重启

随机推荐

  1. Android(安卓)控件架构
  2. android Preference之android:dependency
  3. Android的Message机制(简单小结)
  4. android back 返回机制的几种解决方案
  5. Material Design之FloatingActionBar
  6. Android之最简单的ImageView加边框方法
  7. android 开发 调用第三方程序
  8. Android DEX反编译后部分代码解析
  9. Android中几种延后处理事件的方法
  10. Android(安卓)JNI开发提高篇