Dagger-Android使用手册

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

你可能感兴趣的:(Dagger-Android使用手册)