Dagger2 知识梳理(3) - 使用 dependencies 和 @SubComponent 完成依赖注入

Dagger2 系列文章

Dagger2 知识梳理(1) - Dagger2 依赖注入的两种方式
Dagger2 知识梳理(2) - @Qulifier 和 @Named 解决依赖注入迷失
Dagger2 知识梳理(3) - 使用 dependencies 和 @SubComponent 完成依赖注入
Dagger2 知识梳理(4) - @Scope 注解的使用


一、前言

Dagger2 知识梳理(1) - Dagger2 依赖注入的两种方式 中,我们介绍了两种依赖注入的方式,其中第二种是通过给注入器Component指定Module,再由Module提供创建实例的方法,来实现依赖注入,这是一种“自给自足”的方式,在这种情况下,我们的Component是这么声明的:

指定 Module

除此之外,还有另外两种方式,让Component可以通过其他的Component完成依赖注入,可以划分为:依赖方式继承方式

  • 通过依赖方式实现时,需要在Component中指定它所依赖的Component名字:
    依赖方式下的 Component

    而被依赖的Component需要声明它可以提供哪些类的实例化,因此它需要在Component中声明一些返回值为这些类的方法:
    依赖方式下的被依赖 Component
  • 通过继承方式时实现时,子Component类需要用@SubComponent声明,这种情况下,它一般类似于下面这样:
    继承方式下的子 SubComponent

    并且我们不能直接创建该Component的实例来完成依赖注入,而是需要由父Component实例提供一个getXXX来将这个SubComponent返回给使用者,SubComponent自动拥有了父Component注入对象的能力。
    继承方式下的父 Component

依赖方式和继承方式的共同点就是,它们都是通过别的Component来完成依赖注入的。下面,我们先通过两个简单的例子,来演示如何使用这两种方式完成注入,这篇文章的完整代码可以从 Dagger2Sample 的第三章获取。

二、使用依赖方式完成注入

2.1 定义被依赖的 DependencyComponent

当采用“依赖方式”来实现时,首先需要定义一个被依赖的Component,我们用DependencyComponent来实现,它可以提供一个数据源,即DependencySource

public class DependencySource {
    public String getData() {
        return "获取到来自依赖 Component 的数据";
    }
}

和前面介绍的一样,该数据源通过一个Component和一个Module来提供:

@Component(modules = DependencyModule.class)
public interface DependencyComponent {
    DependencySource getDependencySource();
}
@Module
public class DependencyModule {
    @Provides
    DependencySource provideDependencySource() {
        return new DependencySource();
    }
}

注意上面的DependencyComponent,和前面不同,我们不在它里面声明一个inject接口,而是声明了一个getDependencySource接口,返回值为DependencySource,表示可以给被它依赖的Component提供DependencySource这种类型的实例。

2.2 定义依赖 DependencyComponent 的 SourceComponent

接下来,我们创建一个SourceComponent

@Component(dependencies = DependencyComponent.class)
public interface SourceComponent {
    public void inject(DataRepository dataRepository);
}

Component注解中,采用dependencies来指明它所依赖的Component,接下来,创建一个DataRepository,通过依赖注入的方式来实例化它的DependencySource成员变量,具体的操作步骤为:

  • 通过@Inject声明需要注入的变量mDependencySource
  • 点击make编译
  • 通过DaggerDependencyComponent实例化依赖的DependencyComponent
  • 创建DaggerSourceComponent,在构造的过程中,传入第二步中的DependencyComponent
  • 调用DaggerSourceComponentinject方法完成注入
public class DataRepository {

    @Inject
    DependencySource mDependencySource;

    public DataRepository() {
        //1.实例化所依赖的Component。
        DependencyComponent dependencyComponent = DaggerDependencyComponent.create();
        //2.在构建时传入依赖的Component实例。
        DaggerSourceComponent.builder().dependencyComponent(dependencyComponent).build().inject(this);
    }

    public String getDependencyData() {
        return mDependencySource.getData();
    }
}

2.3 小结

当通过这种依赖于其它Component方式完成注入时,Dagger2会去依赖的Component中查找它是否声明了 返回值为需要实例化的类的方法,如果有,那么就通过该Component来完成注入。

整个流程如下图所示:


依赖方式

对于被依赖的Component,它声明自己可以提供哪些类的实例化,但是并不知道具体有哪些Component需要依赖它。

三、使用继承方式完成注入

3.1 定义父 Component - SourceComponent

在第二节的例子中,我们让SourceComponent依赖于DependencyComponent,完成DependencySource的依赖注入。

下面,我们演示让SourceComponent被其他的Component继承,使得其他Component可以通过它来完成依赖注入,这里唯一的不同,就是该Component需要定义一个getSubSourceComponent()来返回子Component,关于SubSourceComponent3.2中会进行介绍。

@Component(dependencies = DependencyComponent.class, modules = SourceModule.class)
public interface SourceComponent {
    public void inject(DataRepository dataRepository);
    //SubSourceComponent 为子 Component。
    SubSourceComponent getSubSourceComponent();
}
@Module
public class SourceModule {

    @Provides
    LocalSource provideLocalSource() {
        return new LocalSource();
    }
}
public class LocalSource {

    public String getLocalData() {
        return "获取到本地数据";
    }
}

3.2 定义子 Component - SubSourceComponent

下面,我们来定义SubSourceComponent,与之前的Component不同,在接口上,加上的是@SubComponent注解,并且不需要给它指定一个Module,因为它是通过父Component来实现实例化对象的。

@Subcomponent
public interface SubSourceComponent {
    public void inject(SubRepository subRepository);
}

接下来,需要通过以下几步来完成依赖注入:

  • 在需要注入的变量上加上@Inject标签
  • 点击make,完成一次编译
  • 首先实例化父SourceComponent
  • 通过在3.1SourceComponent定义的getSubSourceComponent获取到SubSourceComponent实例。
  • 调用SubSourceComponentinject方法完成注入。
public class SubRepository {

    @Inject
    LocalSource mLocalSource;

    public SubRepository() {
        //1.实例化所依赖的Component。
        DependencyComponent dependencyComponent = DaggerDependencyComponent.create();
        //2.在构建时传入依赖的Component实例。
        SourceComponent sourceComponent = DaggerSourceComponent.builder().dependencyComponent(dependencyComponent).build();
        //3.获取SubComponent。
        SubSourceComponent subSourceComponent = sourceComponent.getSubSourceComponent();
        //4.完成依赖注入。
        subSourceComponent.inject(this);
    }

    public String getLocalData() {
        return mLocalSource.getLocalData();
    }

}

3.3 小结

通过继承方式来实现依赖注入时,父Component也就是SourceComponent需要声明一个getXXX方法,该方法返回子Component,这个子Component需要加上@SubComponent注解,它不用自己声明Module,而是通过与父Component关联的Module来完成依赖注入。

整个流程图如下所示:


继承方式

四、实例演示

下面,我们用一段小程序来演示上面的运行结果:

public class ComponentActivity extends AppCompatActivity {

    private static final String TAG = ComponentActivity.class.getSimpleName();
    private Button mBtnGetData;
    private Button mBtnGetNetData;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_qualifier);

        mBtnGetData = (Button) findViewById(R.id.bt_get_data);
        mBtnGetData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                DataRepository repository = new DataRepository();
                String data = repository.getDependencyData();
                Toast.makeText(ComponentActivity.this, data, Toast.LENGTH_SHORT).show();
            }
        });
        mBtnGetNetData = (Button) findViewById(R.id.bt_get_net_data);
        mBtnGetNetData.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                SubRepository repository = new SubRepository();
                String data = repository.getLocalData();
                Toast.makeText(ComponentActivity.this, data, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

更多文章,欢迎访问我的 Android 知识梳理系列:

推荐阅读更多精彩内容