为了介绍Dagger2的使用,我们搭建了一个Demo来逐步分析,大家可以在这里 下载源码(https://github.com/dushaofeng/DaggerDemo.git)。
前面一节
《Dagger2教程二之基础使用篇》介绍了Dagger2最简单的使用场景,但是在结尾我们遇到两个困难,即如何在不修改注入类代码的情况下实现注入?如何解决注入类构造方法需要参数的情况?
比如我们有一个BeanNeedParam的对象,其
构造方法中需要传递一个String类型的变量用来初始化mName:
public class BeanNeedParam {
private String mName = null;
public BeanNeedParam(String name) {
this.mName = name;
}
public String getName() {
return mName;
}
}
接下来我们介绍如何在不修改这个类的前提下,如何将其注入到Activity中。
一、有参数的注入类使用方式
为了解决有参数的问题,我们需要对之前的结构进行两点改造,下面分别来进行。
1.1、改造Component
首先我们来改造Component,我们依然使用上一篇中的demo,来将BeanComponent.java改造成如下方式:
@Component(modules = BeanModule.class)
public interface BeanComponent {
void inject(MainActivity activity);
}
为了对比明显,我们再贴出之前的BeanComponent内容:
@Component
public interface BeanComponent {
void inject(MainActivity activity);
}
这个时候就能看出来,改造的唯一地方就是再Component的声明后面多了一句:
(modules = BeanModule.class)
具体作用我们先不用管,稍后介绍,接下来我们再继续进行第二个改造内容。
1.2、创建Dagger中的Module
这个修改准确来说是创建一个新的文件,也就是Module文件。
在上面1.1的对Component的改造中我们发现添加了modules = BeanModule.class的语句,这里面的
BeanModule就是我们要添加的文件,文件名随便起,只需要前后对应即可,我们这里就用BeanModule来介绍:
@Module
public class BeanModule {
@Provides
public BeanNeedParam providerBean() {
BeanNeedParam bean = new BeanNeedParam("BeanWithParam");
return bean;
}
}
这就是Module的全部内容,里面有两点需要特别注意:
1、该文件引入了
@Module和@Provides两个新的注释
2、在providerBean中完成了BeanNeedParam对象的创建工作,并传递了合适的参数
1.3、注入对象的使用
经过以上两点改造,我们就可以在Activity中直接使用BeanNeedParam对象了,这里无需修改,继续之前的使用即可:
public class MainActivity extends AppCompatActivity {
//带参数的注入
@Inject
BeanNeedParam mBeanNeedParam;
......
/*
使用Dagger方式创建BeanForDagger对象
*/
private void testDagger() {
// 触发Dagger机制
DaggerBeanComponent.create().inject(this);
if (mBeanNeedParam != null) {
Log.d(TAG, "mBeanNeedParam Name:" + mBeanNeedParam.getName());
}
}
}
这里的使用方式和之前无需参数的注入方式完全相同。
二、注入过程介绍
我们在之前无参数的注入过程基础上来介绍有参数的情况下注入过程。
通过之前的介绍,在Activity中通过DaggerBeanComponent.create().inject(this)触发了Dagger的注入机制。
然后Dagger扫描当前Activity有个BeanNeedParam的对象需要被注入:
//带参数的注入
@Inject
BeanNeedParam mBeanNeedParam;
然后按照之前的介绍,Dagger将会在BeanNeedParam的类中搜索使用@Inject注释了的构造方法,并用他来创建BeanNeedParam对象。
但是这里的BeanNeedParam中的构造方法没有使用@Inject来注释,说明无法直接注入。
接下来Dagger就会在Component中搜索被注册的modules有哪些,结果找到了BeanModule.class。
然后在BeanModule.class中搜索同时具备如下条件的注入类:
1、通过@Provides注释的方法
2、该方法必须是public属性
3、该方法的返回值是BeanNeedParam
结果就找到了providerBean()方法,然后就调用该方法并顺利拿到了BeanNeedParam对象,然后交给Activity的mBeanNeedParam使用。
请注意,Dagger在Module中搜索目标类时,
与Module中提供的方法名无关,只和返回值有关,比如下面两种写法是完全等效的,都可以实现BeanNeedParam的注入:
@Provides
public BeanNeedParam providerBean() {
......
}
和:
@Provides
public BeanNeedParam dushaofeng() {
......
}
整个过程可以归纳为以下步骤:
1、Activity中通过DaggerXXXComponent的Inject()触发注入过程
2、Dagger在Activity中搜索用@Inject标注的变量,说明改对象需要被注入
3、去Component中注册的Module中搜索注入类
4、在Module中搜索返回值为注入类的方法,执行并拿到注入类对象,从而完成注入过程
5、如果在Module中没有搜索到提供目标类注入的方法,则在工程中搜索目标类
6、找到需要注入对象后,寻找该对象中用@Inject标识的构造方法,完成自动创建过程
三、Module中方法需要参数的情况
如果遇到Module中的方法需要参数的情况该如何处理呢?
比如Module中提供BeanNeedParam的方法是这样的:
@Provides
public BeanNeedParam providerBean(String name) {
BeanNeedParam bean = new BeanNeedParam(name);
return bean;
}
那么Dagger在调用该方法时,就需要一个String类型的参数,如何将这个参数传递给Dagger呢?
可以这样实现,在Module中提供一个返回值为String的方法:
@Module
public class BeanModule {
@Provides
public BeanNeedParam providerBean(String name) {
BeanNeedParam bean = new BeanNeedParam(name);
return bean;
}
@Provides
public String providerString(){
return "param from other function";
}
}
也就是说,Dagger在Module中执行providerBean时,发现需要一个String类型的参数,接着Dagger就会在Module中搜索返回值为String的方法并执行。
这说明Dagger在Module中的执行也具备递归性。
四、一个疑问
通过上面的介绍我们找到了不修改注入类代码的情况下将其注入的方法。但这种方法还是有个局限:那就是每次注入只能传递给BeanNeedParam相同的参数,当我们需要给他传递不同的参数时,如何实现呢?
比如对于BeanNeedParam的注入类来说,我们需要在不同场合传递给他不同的name值,该如何处理?
这个问题下一篇
《Dagger2教程四之多构造方法的情况》来介绍。