What
关于IOC(控制反转)
和DI(依赖注入)
,DI
只是IOC
的实现的一种方式,另一种是依赖查找
,详细见 控制反转维基百科
完全静态的依赖注入框架。依赖注入的概念真的是到哪都有,以前学Spring的就天天看它,到了android还是跑不了,现在的dagger是以半静态注入的形式,说白了,通过注解处理器分析注解,生成java代码,将对象池和需要对象的地方连接起来,其实就和自己创键差不多,我就简单总结下怎么用吧(里面的概念我也挺晕乎的,注解处理器那里我也是挺头疼的。)
官网: Dagger users-guide (果然还是还是官方文档讲的好呃呃)
How
引入依赖
1 | implementation 'com.google.dagger:dagger:2.20' |
最简单的例子
首先需求是我需要一个对象。使用@Inject
注解构造方法表示构造器注入
1 | public class Apple { |
编写注入器,用@Component
注解的一个接口即可,里面有一个inject
方法,参数类型是你要注入的地方。
1 |
|
注入,构建项目,然后生成许多类,我们只用在意形如DaggerAppleComponet这样以Dagger开头的生成器就行了。
同样用@Inject
注解我要注入的对象,DaggerAppleComponet.builder().build().inject(this);
这样就完成注入了
1 | public class TestActivity extends AppCompatActivity { |
在依赖注入里面是会自动解决依赖的。比如
1 | class A { |
此时A依赖B,在注入A的时候会自动取注入B。只需要二者存在注入形式就行。
使用module注入
构造器注入挺简单方便,但是不是所有的对象都能够构造器注入,比如第三方类实例,你就无法添加注解,还有比如Retrofit中动态生成的接口API对象,也不能。所以注入module
登场,在这里面用于产出对象。
使用@Module
注解类名,就表示这是一个module,可以有构造函数传入我们需要的依赖,比如Context
。
使用@Provides
修饰方法,命名为:providesXX
,就表示产出对象。
1 |
|
同样需要声明注入器
和上面不同的是需要,写入Module类型
1 | (modules = {CommonModule.class}) |
注入
1 | public class TestDagger2Activity extends AppCompatActivity{ |
这只是另一种注入方式而已。
注解说明
@Component
注解接口,标注为注入器,构建后会生成前缀Dagger的类,用于注入
@Module
注解类,标注为注入模型,用于生成模型注入对象
@Inject
注解构造方法表示,构造器注入
注解变量,表示被注入的引用,访问限定不能为private
不可以修饰接口,即使是通过Provides产出的接口类型,也只是能够通过方法依赖传递,并不能注入到标注了@Inject的引用上去
因为我遇到了过这个问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23//大概举例
interface A {}
//通过Retrofit生成A
A providesA(Retrofit retrofit) {
return retrofit.create(A.class);
}
//此时我们是无法通过@Inject获得A的
//无效 A;
//包装下
class DataModel {
A mA;
DataModel(A a) {
mA = a;
}
public A getA() {
return mA;
}
}
//此时我就可以这样
DataModel mModel;
A a = mModel.getA();
@Provides
在模型中,注解方法,一般规范以provides前缀开头方法,表示产出某对象
@Named
可能存在同种类型的对象,需要通过命名区分,其实@Named也是通过@Qualifier定义的。
1 | "one") ( |
方法可以是静态的,这就意味着Module不需要实例化传入compoent
@Qualifier
修饰注解,来标注具体那个对象
1 |
|
@Scope
修饰注解,用于生成标注注入对象的作用域单例的注解
例如以下就生成了一个@PerFragment的作用域注解,在这个作用域内,@PerFragment修饰的对象为单例,即局部单例,亦或者搭配限定符使用,产出不同限定的单例
1 |
|
然后将该注解用于修饰注入器,和产出对象的地方
1 |
|
@Singleton
这个注解也是官方通过@Scope实现的,直接表明单例
1 |
|
懒加载
默认会在调用前就将对象注入到引用,使用以下形式可以懒加载,不会立马注入对象,只有在lazy.get()时才会去获得对象
1 |
|
Provider
有时候许多要不断注入同一类型的对象比如
1 | A a1; |
像这样需要不止一个对象,这时候就需要使用Provider了
1 | Provider<Filter> filterProvider; |
Componet注入器之间关系
之前的简单场景都是仅限于当前注入环境,倘若我们又这个需求,比如Fragment需要用到Activity的对象,亦或者全局环境需要用到全局注入对象,这个时候前面的基础操作就不管用了。因为它只关心注入的当前环境Tinject(T)
,离开了这个环境你也就无法获得注入对象了。
所有我们就得需要以下几个概念
注入器之间相互依赖
依赖就是,A的工作需要B的帮忙,也就能名正言顺的获得B注入器的注入对象
1 | A(B b); |
在注入器我们可这样声明依赖的注入器,通过在@Componet添加依赖的注入器,可以添加很多的
1 |
|
注入过程,既然是已依赖的方式注入,就得要拿到依赖的注入器对象。
1 | DaggerLoginFragmentComponent.builder() |
如果你想获得依赖的对象,必须要在注入器暴露接口,才能打开一个通往注入器依赖的入口
1 |
|
这样就能名正言顺的使用依赖注入器的依赖对象了。
注入器继承
第二种方式,比如一个Activity有很多的Fragment,这个时候使用继承关系来获取注入器内容更好,不需要暴露依赖对象接口
看父注入器声明
1 |
|
子注入器声明,使用@SubComponet表明这个子注入器
1 |
|
注入
1 | ((MainActivity) mActivity) |
通过继承的方式将应用对象图谱分割成不同的部分,子注入器可以依赖祖先注入器的绑定对象,反之父注入器不能依赖子注入器绑定对象,同级注入器也无法依赖。换句话说父注入器的对象图是子注入器的对象图的子对象图。
呃呃好像官方文档使用Builder另一种方式写的SubComponent
这种基于Builder写起来真麻烦啊
在Component提供依赖
这样主动暴露依赖,其实去工厂里拿
1 | @Componet |
Multibindings
允许将绑定对象进集合
绑定进Set
使用@IntoSet
表示将绑定对象输入set
1 |
|
使用@ElementsIntoSet
表示将一个set输送进set
1 |
|
绑定好的set可以解决set的依赖
1 | class Bar { |
或者这样使用
1 | (modules = {MyModuleA.class, MyModuleB.class}) |
可以结合 Set<Foo>
以及 Provider<Set<Foo>>
or Lazy<Set<Foo>>
这样的形式,但是不能Set<Provider<Foo>>
同样可以结合@Qualifier
来限定
绑定进Map
还可以绑定进map,好吧本来我想看到上面就写的。下面其实就是看到官方文档上的23333
普通使用
使用@IntoMap
注解进入map的对象,使用@StringKey
表示String类型的key,使用@ClassKey
表示Class类型的key
之后我们就可获得Map<String, Long>
的Map
1 |
|
枚举和带泛型的KEY
通过@MapKey
来自定以枚举key,以及符合泛型通配的key
1 | enum MyEnum { |
更加复杂的KEY
1 | false) (unwrapValue = |
对于复杂的Key来说,编译时是无法具体确认的,考虑使用Set<Map.KeyEntry>
来完成同样可以达到map效果
上面的只是讲了个大概目前用到,其实还有关于子注入器更多的知识和Producers的内容,以后再补充吧。
Why
参考