依赖注入咯
依赖注入
原理:
依赖注入是实现程序解耦的一种方式。如果通过百度搜索可以找到如下答案:
控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题.控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。依赖注入应用比较广泛。
个人理解:
在程序中,一个对象中的方法需要依赖另一个对象,该对象中保存其所依赖对象的实例,生成依赖对象的方式不再该对象中通过new创建,而是调用者外部创建依赖对象,通过一定的方式进行传入。
依赖注入的三种实现方式(原生方式):
- 构造方法注入 该方式是通过构造方法将其所依赖的外部类对象传入进来,是我认为的最简单的方式。其实现方式如下,我们修改之前的代码:
1 | public class Classes { |
- Setter 注入:
1 | public class Classes { |
- 接口方式 接口方式是定义一个接口,该接口中声明一个注入的方法,而需要注入的类实现该接口,实现接口中定义的方法。定义接口,声明注入方法:
1 | public interface BoyInjection { |
Android Dagger2依赖注入:
- 引入依赖库:
1 | compile 'com.google.dagger:dagger:2.11' |
版本号以官网为准:https://github.com/google/dagger
- 创建一个对象:
1 | public class A { |
- 创建Moudule:
1 | //第一步 添加@Module 注解 |
- 创建具体的示例:
1 | //第一步 添加@Module 注解 |
- 创建一个Component:
1 | //第一步 添加@Component |
之后Rebuild Project一下,Make Project也行,之后系统会自动创建几个类,用来与activity连接。(这一步操作可能会要等待很长一段时间,10分钟左右)
- 将Component与Activity/Fragment绑定关系:
1 | public class MainActivity extends AppCompatActivity { |
之后就可以了,接下来介绍Dagger2的单例模式.
单例模式:
@Singleton 这个注释可以使Dagger进入单例模式,要在Moudule和Component的上方添加上
1 | @Module |
添加@Singleton就变成单例了,在MainActivity里 声明两个Student不同名字的对象分别打印他的地址,你会发现地址是一样的
但是你再新建立一个Activity 再次声明 Student stu ,注入对象,你会发现这时候 地址就不一样了。因为持有的对象是Activity。
到此已经能在一个Activity里,声明两个对象变量,用@Inject注入,得到的是 同一个对象
@Singleton的分析:
接下来我要推翻刚才说的@Singleton,看到英文Singleton 是单例的意思,在Dagger2里他就是个命名的一个注解而已
他叫什么名字都行 @ABC、@ABB、@ABCC,都行。点开Singleton(ctrl+B)
1 | @Scope //作用域的注释,代表着被包括的都是在一个作用域之中 |
Singleton 用了@Scope(作用域) 标记。 也就是说被 @Scope包裹的Component 都是在一个作用域里的, 作用域!不叫单例
好 那直接用Scope包裹Component。编译器提示 直接Remove了这个注解。因为他的目标是 @Target(ANNOTATION_TYPE) 这个表示这个Scope只用于注解,不能用在 类上面
那我们声明一个注解:
1 | @Scope |
接下来我们把刚才加@Singleton的地方全换成@ActivityScope
1 | @Module |
打印两个对象
1 | tag: com.mydagger.bean.Student@96575e0 |
刚才说了持有对象的是Activity,那只能在一个Activity中实现单例,那要全局单例怎么做呢?
通常要全局使用的对象,要么写个static的类,要么Application。我们直接Application
全局单例:
先定义个全局的注解,刚讲了名字无所谓(这里要创建第二个的注解是因为,一个注解只能作用到一个作用域,简单来说就是不能与等下要创建的的另一个Component的注解相同)
1
2
3
4@Scope //作用域的意思
@Retention(RetentionPolicy.RUNTIME)
public @interface AppScope {
}修改代码
1
2
3
4
5
6
7
8
9@ActivityScope
//先绑定自身的Module,在绑定要依赖的Component
@Component(modules = {ActivityModule.class},dependencies = AppComponent.class)
public interface ActivityComponent {
//第一个activity的绑定方法(XXXActivity就是填写你自己的activity的名字)
void inject(XXXActivity testActivity);
//第二个activity的绑定方法
void inject(XXXActivity testActivity);
}这里增加了一个 dependencies(依赖),当前是ActivityComponent 依赖 AppComponent
新建一个 AppComponent, 连接一个Module,提供一个方法 提供 给 MainActivity @Inject使用
还有就是dependencies只是依赖与另一个Component,每个Component都必须要有一个不同的Modules(Modules中的bean类不能重复)1
2
3
4
5
6
7//注意这里的注解与上面注解不同(尽管效果是一样的,但也不能一样)
@AppScope
@Component(modules = {AppModule.class})
public interface AppComponent {
//这里要重新创建一个School类(这里写的方法要与Module中的类一致,否则就会报错)
School getSchool();
}
新建 AppModule, 提供一个方法 用AppScope和Provides标记
1 | @Module |
继续点绿锤子编译修改Application
1 | //创建一个自身的单例类 |
这里返回一个Application的实例对象。定一个AppComponent(这个是准备给DaggerActivityComponent依赖的)也是Dagger生成的代码。链式编程进行build
增加一个School类 和Student类一样 用@Inject修饰构造函数。
修改MainActivity,增加
1 | @Inject |
然后在onCreate后面增加(与activity和Fragmemt进行绑定)
1 | DaggerActivityComponent.builder() |
第二行DaggerActivityComponent 依赖于 Application的 AppComponent 这样就进行了关联
到此 全局单例 搞定,测试一下 写两个Activity 声明相同的对象就会发现 两个对象地址 一样的(创建的第二个activity/Fragment中也要进行Component绑定)
1 | tag: com.mydagger.bean.School@393c7e3 |
- 扩展:
通常会项目中使用MVP 增加网络配置等 需要全局单例的内容,可以继续建类(Module)进行扩展,得到单例1
2
3
4
5
6
7
8
9
10
11
12@AppScope
@Component(modules = {AppModule.class,BModule.class....})
public interface AppComponent {
School getSchool();
//这里需要继续提供方法,给Activity 进行对象的 @Inject
}
applicationComponent = DaggerAppComponent
.builder()
.appModule(new AppModule())......
.build();