Dagger2 使用正确姿势。
上一篇文章《Dagger2 这次入门就不用放弃了》中介绍了Dagger2的一些显浅的使用方式,我觉得是非常适合入门Dagger2
的傻瓜式讲解,后来发现有些内容讲的不够仔细,有些细节没有详细解释清楚。参考了以下三篇文章后,对之前的内容进行一些补充。
以上这三篇文章对于Dagger2
的思路理得很清晰,并且对于Dagger2
做出了一个抽象的概念讲解,本文的一些内容也是摘自以上文章。
没有看过《Dagger2 这次入门就不用放弃了》可能不利于阅读
本文的内容:
本篇的内容同样不涉及到Dagger2
的内部原理,只是对上一篇文章做了一些知识的补充,从而能够更好的使用Dagger2
- 理清思路,
Dagger2
的两种提供注入实例的方式 - 为什么
ActivityComponent
要提供一个inject
方法。 Scope
的使用,如何实现单例?Qualifier
限定符的作用以及使用。
理清思路,Dagger2的两种提供注入实例的方式。
之前说的是提供注入实例的方式是通过编写Moudle
,并且提供一些provideXXX()
的方法,然后通过Component
把这些对象进行注入。
其实在Dagger2
中还有一种方式实现对象的注入,这种方式比较简单。假设我们需要注入一个A类的对象,我们只需要在A类的构造函数中添加@Inject
注解即可,然后在Component
中直接提供一个方法获取A的实例就行了。代码如下:
public class A{
@Inject
public A(){
}
}
//在 XXXComponent 中
A getA();
然后在我们需要注入A对象的地方(Activity)
,使用Component
调用getA()
方法即可获得这个A对象。
说到这里,可能也会有些疑问,这里的A类是一个简单的对象,假如A需要在构造函数传入参数怎么办?其实原理和上一篇文章说的差不多,这些参数会根据依赖的Component
或者当前Component
的module
中提供的provideXXX()
方法来获取。
这里的Dagger2
总共就有这两种方式提供依赖的注入。事实上,经过测试,Dagger2
在遇到@Inject
修饰的成员变量的时候,会先查找Component
中是否有提供对象,找不到的情况下,才会去找包含有@Inject
的构造函数。
整个Dagger2的
依赖注入的过程如下:
步骤1:查找Module中是否存在创建该类的方法。
步骤2:若存在创建类方法,查看该方法是否存在参数
步骤2.1:若存在参数,则按从**步骤1**开始依次初始化每个参数
步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
步骤3:若不存在创建类方法,则查找Inject注解的构造函数,
看构造函数是否存在参数
步骤3.1:若存在参数,则从**步骤1**开始依次初始化每个参数
步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
为什么ActivityComponent要提供一个inject方法
首先经过我的测试,当我在DaggerActivity
中对对某些需要注入的成员添加@Inject
注解的时候,Dagger2
就会生成一个DaggerActivity_MembersInjector
的东西,这个是对成员变量进行注入的关键类,涉及到一些比较深入的内容,我们这里先不要深入探讨这里的运行机制。我们只需要知道当我们需要注入一些成员变量的时候,我们需要在ActivityComponent
中提供一个方法:
void inject(DaggerActivity daggerActivity);
并且在初始化的时候调用这个方法,才能成功注入。至于这个方法inject
是官方推荐的名称,我们可以不用这个,但是使用inject
的话比较好理解吧。值得注意的一点是,这个方法的参数一定要准确,比如DaggerActivity
需要注入,我们这里就需要些DaggerActivity
,不能写成BaseActvity
或者Activity
,否则会注入失败。因为内部的原理是要对这个参数进行注入的,很显然在DaggerActivity
中需要注入的内容在BaseActivity
或者Activity
中并不存在。
Scope的使用,如何实现单例?
这个迷之Scope
也是有点难以理解,我们在哪里使用到了Scope
呢。在我们的AppComponent
中添加了一个注解为@Singleton
,@Singleton
就是一个Scope
,据说可以实现单例哟。。。难道这样就实现了单例模式?我刚刚开始是这样理解的。直到仔细的看了几遍这篇文章我才知道并不是这样的。
事实上@Sinleton
中并没有创建单例的能力,那么AppComponent
中提供的依赖注入是如何实现单例的呢。其实这个原理很贱单。
首先Module
提供了创建实例的方法,接着AppComponent
中对Module
进行管理,最后AppComponent在自定义Applicaiton中被实例化了一次。
这个实例化了一次是最重要的呀。仅仅被实例化了一次,那不就是单例么。就是这么简单呀。
可能有些童靴当时就不乐意了,那既然这样都已经实现了单例,那么这个@Singltop
还要来何用?不是多此一举吗。
其实@Singletop
还有有一些作用的,首先一方面能让你直面的了解到这是一个单例,其次这个@Singletop
能够更好的管理Modlue
和Component
之间的关系。
Dagger2
需要保证Component
和Module
是匹配的,就需要用到这个注解。
为什么这样说,看过上一篇文章的人就会知道,我定义了一个ActivityScope
,我之前的解释是说ActivityScope
是为了对应Activity
的生命周期(当时就很不理解,难道这个Scope
真的这么有魔性,声明一个ActivityScope
就能让提供的注入对象和Activity生命周期一致),很显然,这是错误的。为什么需要这个Scope
呢?原因是因为我在AppComponent
中是有@Singletop
的,ActivityComponent
中依赖了AppComponent
,所以我们需要使用一个Scope来
匹配他们之间的关系,不然就会在编译期间报错。并不是说ActivityScope
能让实例和Activity
生命周期一致。和Activity
生命周期一致是因为ActivityComponent
是在Activity
中生成实例的。(具体Dagger2
内部是如何保障这个的,并没有深入研究过)。
Qualifier 限定符的作用以及使用。
这也是一个很强大的注解,首先为什么需要用这么一个东西呢,之前说道过,在Module
中的provide
方法实际上是根据返回值来进行识别的。但是假设我需要根据不同的需求传入不同的构造参数的时候,如何区分呢?比如:一个Presenter
,可能他有两个构造函数,分别对应不同的需求,这种情况下,provide
方法的返回值都是Presenter
,那么就需要使用Qualifier
。具体怎么使用呢。
首先声明一个注解用@Qualifier
修饰,然后在需要区别的地方添加就行了,下面是一个实例代码:
@Module
public class AppModule {
private final App app;
public AppModule(App app) {
this.app = app;
}
@Provides
@Singleton
@ForApplication
Context provideAppContext() {
return app;
}
@Provides
@Singleton
Prefser providePrefser(@ForApplication Context context) {
return new Prefser(context);
}
@Provides
@Singleton
AccountManager provideAccountManager(@ForApplication Context context) {
return AccountManager.get(context);
}
首先在provide中添加一个注解@ForApplicition
,接着在需要使用这个Context
的地方再次标示即可。这样假设有其他一些提供了Activity
的Context
的地方和这里发生冲突的时候,Dagger2
也能准确找到这个Applicaiton
的Context
。
总结
这里对Dagger的总体使用方式进行了一些补充,还有没有从源码的角度来理解Dagger2,下篇文章会结合Dagger2生成的代码进行讨论,只有认识了这个运行机制,我们才能运用得得心应手。