简介
Dagger-匕首,鼎鼎大名的Square公司旗下又一把利刃(没错!还有一把黄油刀,唤作ButterKnife)
Dagger2 是一个Android依赖注入框架,由谷歌开发,最早的版本Dagger1 由Square公司开发。依赖注入框架主要用于模块间解耦,提高代码的健壮性和可维护性。Dagger 这个库的取名不仅仅来自它的本意“匕首”,同时也暗示了它的原理。
Android开发从一开始的MVC框架,到MVP,到MVVM,不断变化。现在MVVM的data-binding还在实验阶段,传统的MVC框架Activity内部可能包含大量的代码,难以维护,现在主流的架构还是使用MVP(Model + View + Presenter)的方式。但是 MVP 框架也有可能在Presenter中集中大量的代码,引入DI框架Dagger2 可以实现 Presenter 与 Activity 之间的解耦,Presenter和其它业务逻辑之间的解耦,提高模块化和可维护性。
为什么使用依赖注入
首先我们需要知道,人们在很长的一段时间里都是利用控制反转原则规定:应用程序的流程取决于在程序运行时对象图的建立。通过抽象定义的对象交互可以实现这样的动态流程。而使用依赖注入技术或者服务定位器便可以完成运行时绑定。
使用依赖注入可以带来以下好处:
依赖的注入和配置独立于组件之外。
因为对象是在一个独立、不耦合的地方初始化,所以当注入抽象方法的时候,我们只需要修改对象的实现方法,而不用大改代码库。
依赖可以注入到一个组件中:我们可以注入这些依赖的模拟实现,这样使得测试更加简单。
可以看到,能够管理创建实例的范围是一件非常棒的事情。按我的观点,你app中的所有对象或者协作者都不应该知道有关实例创建和生命周期的任何事情,这些都应该由我们的依赖注入框架管理的。
什么是JSR-330?
为了最大程度的提高代码的复用性、测试性和维护性,Java的依赖注入为注入类中的使用定义了一整套注解(和接口)标准。Dagger1和Dagger2(还有Guice)都是基于这套标准,给程序带来了稳定性和标准的依赖注入方法。
Dagger1
这个版本不是这篇文章的重点,所以我只是简略地说一下。不管怎样,Dagger1还是做了很多的贡献,可以说是如今Android上最流行的依赖注入框架。它是由Square公司受到Guice启发创建的。
基本特点:
多个注入点:依赖,通过injected
多种绑定方法:依赖,通过provided
多个modules:实现某种功能的绑定集合
多个对象图: 实现一个范围的modules集合
Dagger1是在编译的时候实行绑定,不过也用到了反射机制。但这个反射不是用来实例化对象的,而是用于图的构成。Dagger会在运行的时候去检测是否一切都正常工作,所以使用的时候会付出一些代价:偶尔会无效和调试困难。
Dagger2
Dagger2是Dagger1的分支,由谷歌公司接手开发,目前的版本是2.0。Dagger2是受到AutoValue项目的启发。 刚开始,Dagger2解决问题的基本思想是:利用生成和写的代码混合达到看似所有的产生和提供依赖的代码都是手写的样子。
如果我们将Dagger2和1比较,他们两个在很多方面都非常相似,但也有很重要的区别,如下:
再也没有使用反射:图的验证、配置和预先设置都在编译的时候执行。
容易调试和可跟踪:完全具体地调用提供和创建的堆栈
更好的性能:谷歌声称他们提高了13%的处理性能
代码混淆:使用派遣方法,就如同自己写的代码一样
当然所有这些很棒的特点都需要付出一个代价,那就是缺乏灵活性,例如:Dagger2没用反射所以没有动态机制。
小讲解—降低耦合度的方法
一旦 new出实例。那么就产生耦合
比如
A
new a
B
new b
C
new c
检验耦合度的方法
当我们删除 abc时 发现A,B,C都报错了。
而当我们使用工厂模式管理起 a b c的实例时。
删除 abc时 发现 只有Factory类编译报错。
三个类与一个类相比 当然是前者的耦合度高。
我们这里说的是降低耦合度,而不是去掉耦合。
环境搭建
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
}
}
android {
...
}
#module
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
//dagger start
apt 'com.google.dagger:dagger-compiler:2.0'
//dagger end
compile 'com.google.dagger:dagger:2.0'
compile 'org.glassfish:javax.annotation:10.0-b28'
compile 'com.google.android.gms:play-services:8.4.0'
...
}
@Inject与Compoent使用小结
用Inject注解标注目标类中其他类
用Inject注解标注其他类的构造函数
若其他类还依赖于其他的类,则重复进行上面2个步骤
调用Component(注入器)的injectXXX(Object)方法开始注入(injectXXX方法名字是官方推荐的名字,
以inject开始)
public class UserInfo {
@Inject
public UserInfo() {
}
public String name="黑马";
}
public class MyActivity extends AppCompatActivity {
@Inject
UserInfo info;
使用@Component申明注入器
@Component
public interface MyActivityComponent {
void inject(MyActivity activity);
}
调用apt编译后的Component
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
//使用Component注入
DaggerMyActivityComponent.builder().build().inject(this);
}
@Module与@Provider使用小结
有Inject与Component不就够了?为啥又造出个Module?
现在有个新问题:项目中使用到了第三方的类库(比如是jar包类型的),不能修改,
所以根本不可能把Inject注解加入这些类中,这时我们的Inject就失效了。
Inject,Component,Module,Provides是dagger2中的最基础最核心的知识点。
奠定了dagger2的整个依赖注入框架。
Inject主要是用来标注目标类的依赖和依赖的构造函数
Component它是一个桥梁,一端是目标类,另一端是目标类所依赖类的实例,
它也是注入器(Injector)负责把目标类所依赖类的实例注入到目标类中,同时它也管理Module。
Module和Provides是为解决第三方类库而生的,Module是一个简单工厂模式,Module可以包含创建类实例的方法,
这些方法用Provides来标注
Qualifier(限定符)、Singleton(单例)、Scope(作用域)、Component的组织方式
#
基于同一个维度条件下,若一个类的实例有多种方法