前言
Dagger2是dagger的升级版,dagger是由square公司开发,github链接:https://github.com/square/dagger,不过,它的升级版dagger2,现在由Google接手维护,github链接:https://github.com/google/dagger。
dagger2到底做什么用?简单地说:依赖注入。
在软件工程领域,依赖注入(Dependency Injection)是用于实现控制反转(Inversion of Control)的最常见的方式之一。
为什么需要依赖注入
控制反转用于解耦。
举个栗子
public class A{
...
Class b;
public A(){
b=new B();
}
public void doSomething(){
b.do();
}
...
}
上面的写法我们经常用到,也都是这么写的,那么这么写有什么问题吗?试想一下,如果Class B发生变化了,比如构造方法需要参数,或者do()方法需要传参数,那么我们就不得不改Class A的构造方法,所有用到A实例的地方就都需要修改了。这样,AB就仅仅耦合在一起了,对以后程序的扩展修改不利。开闭原则告诉我们程序应该是对外扩展开放,对修改关闭,这正好与这一原则相违背。
依赖注入的实现方式
我们的代码中有很多依赖注入,只不过我们没有察觉罢了。依赖注入主要有以下集中方法:
- 构造函数注入
public class ClassH {
ClassB classB;
public ClassH(ClassB b){
this.classB=b;
}
}
- setter方法注入
public class ClassH {
ClassB classB;
public void setClassB(ClassB b){
this.classB=b;
}
}
- 通过接口注入
interface IClassH(){
void setB();
}
public class ClassH implement IClassH{
ClassB classB;
@override
void setB(ClassB b) {
classB = b;
}
}
最后,是通过Dagger2的方式注入。下面是通过http://www.jianshu.com/p/8527e3bb9097,MVP模式的登录栗子,用一下Dagger2。
Dagger2的引入
dependencies {
compile 'com.google.dagger:dagger-android:2.x'
compile 'com.google.dagger:dagger-android-support:2.x' // if you use the support libraries
annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'
annotationProcessor 'com.google.dagger:dagger-compiler:2.12'
}
对于Android Studio版本在2.2以下的,用以下的方法引入Dagger2。
- 配置apt插件,在Project的build.gradle下
dependencies {
classpath 'com.android.tools.build:gradle:2.3.1'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
- 添加依赖,在Module下的build.gradle下
apply plugin: 'com.android.application'
//添加如下代码,应用apt插件
apply plugin: 'com.neenbedankt.android-apt'
...
dependencies {
...
compile 'com.google.dagger:dagger:2.4'
apt 'com.google.dagger:dagger-compiler:2.4'
//java注解
compile 'org.glassfish:javax.annotation:10.0-b28'
...
}
Dagger2的使用
具体使用可以参照:https://google.github.io/dagger/android.html,Google的Dagger2官方支持,分Activity和Fragment的inject。
Dagger是基于MVP模式的,不了解MVP模式的请先移步 http://www.jianshu.com/p/8527e3bb9097,这是一个基于MVP模式的登录模块。
下面参考前面的mvp登录模块,一步步使用Dagger2。
以在LoginActivity中注入LoginPresenter为例,大致可以分为5步。
1 在LoginActivity中Inject目标类。
这里我们在LoginActivity中注入了LoginPresenter 的对象。mLoginPresenter,这样就可以不通过new的方式创建实例,达到了解耦的目的。
@Inject
LoginPresenter mLoginPresenter;
这里遇到了第一个注解符@Inject。
注解符
- @Inject:在需要注入依赖的地方使用这个注解,你用它告诉Dagger这个 构造方法,成员变量或者函数方法需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。
注入之后,LoginActivity节持有了LoginPresenter的实例mLoginPresenter,如果这时候你运行一下代码,会报错,NullPointerException,为什么呢?原因是虽然我们Inject了mLoginPresenter,但是我们并没有真正实例化她,那么它在哪实例化呢?在Module里
2 编写Module,为注入时的实例化提供参数
先上代码
@Module
public class LoginModule {
private IView.ILoginView loginView;
public LoginModule(IView.ILoginView view){
this.loginView=view;
}
/**
* 提供LoginPresenter
* @param iLoginView
* @return
*/
@Provides
@Singleton
LoginPresenter provideLoginPresenter(IView.ILoginView iLoginView){
return new LoginPresenter(iLoginView);
}
@Provides
@Singleton
IView.ILoginView provideILoginView(){
return loginView;
}
}
说明
- Module的作用是用来提供生成依赖对象的,生成实例的,你可以把它看成是一个工厂,里面生产各种各样的实例。
- 必须要用@Module注解,说明此类是个Module
注解符
- @Module 用Module标注的类是专门用来提供所有依赖的,里面有几个provideXXX()方法就提供多少个依赖
- @Provides 在Module中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。也就是说,@Provides用在方法上,是提供具体依赖的。
- @Singleton 当前提供的对象将是单例模式 ,一般配合@Provides一起出现
至此我们的实例化过程完成了,下面还有一点要补充说明的
补充
是不是只能用Module提供以来对象呢,不是的,以我们的上面的例子说明,LoginActivity里注入了LoginPresenter对象,如果我们不编写Module,那怎么生成实例呢。
@Inject
LoginPresenter mLoginPresenter;
答案是到LoginPresenter的构造方法前,加上@Inject注解。如下代码所示
@Inject
public LoginPresenter(IView.ILoginView iLoginView) {
this.mILoginView = iLoginView;
mLoginModel = new LoginModel();
}
这样也可以完成实例化过程。
也许你要问,既然这样可以完成实例化,为么还要编写Module呢?
答案是:
- 如果项目中使用到了第三方的类库,第三方类库又不能修改,所以根本不可能把Inject注解加入这些类中,这时我们的Inject就失效了。
- 如果我们以这种方法提供实例化,那么实例化过程会分散在整个项目中,而Module正好可以将它们集中起来管理。
整个注入,实例化的过程可以用下面的步骤描述:
当遇到@Inject注解时:
步骤1:查找Module中是否存在创建该类实例的方法。
步骤2:若存在创建类实例方法,查看该方法是否存在参数
步骤2.1:若存在参数,则按从**步骤1**开始依次初始化每个参数
步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
步骤3:若不存在创建类方法,则查找Inject注解的构造函数(构造函数),
看构造函数是否存在参数
步骤3.1:若存在参数,则从**步骤1**开始依次初始化每个参数
步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
现在实例已经构建好了,现在我们有了(1)依赖的使用方:LoginActivity,通过 @Inject注入(2)依赖的提供方Module。
怎么才能在两者之间建立桥梁呢,答案是Component
3 编写Component
Component一般用来标注接口,如上文所说,作用在于作为依赖提供方和依赖使用方沟通的桥梁。
@Singleton
@Component(modules = {LoginModule.class})
public interface LoginComponent {
void inject(LoginActivity loginActivity);
}
- 一个类要想是Component,必须用Component注解,并且该类是接口或抽象类,并通过(modules = xxx.class)绑定对应module,并声明一个inject方法,在依赖的使用方会用到
- @Inject和@Module的桥梁,它的主要作用就是连接这两个部分
- 我们要列出所有的@Modules组成该组件,如果缺失了任何一块都会在编译的时候报错
4 编译。AndroidStudio -> Build -> Make Project
编译后,MainComponent生成一个DaggerXXXComponent类,这个类我们最后一步用到。
5 在Activity中注入
DaggerLoginComponent.builder()
.loginModule(new LoginModule(this))
.build()
.inject(this);
效果是一样的
网友评论