1.导入依赖
implementation 'com.google.dagger:dagger:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
最新版本请官网获取
2.什么是Dagger2?
- Dagger2一个谷歌开源的依赖注入(
Dependency Injection
)框架,简称DI
。Dagger2 是 Google 出的依赖注入框架。肯定有小伙伴疑问,为什么会有个 2 呢。该框架是基于 Square 开发的 Dagger 基础上开发的。 - Dagger2主要用于程序之间的解耦,程序的耦合性越低表明这个程序的可读性以及可维护性越高。控制反转(
Inversion of Control或IOC
)就是常用的面向对象编程的设计原则,使用这个原则我们可以降低耦合性。其中依赖注入是控制反转最常用的实现。
3.为什么使用Dagger2?
常用的依赖注入
- 构造器注入
public class A {
private B b;
A(B b) {
this.b = b;
}
}
- setter方法注入
public class A {
private B b;
public void setB(B b) {
this.b = b;
}
}
- 接口注入
//首先定义一个接口
public interface IC {
void printMess();
}
//A
public class A implements IC {
@Override
public void printMess() {
System.out.println("我的信息是A");
}
}
//B
public class B implements IC {
@Override
public void printMess() {
System.out.println("我的信息是B");
}
}
//D
public class D {
private IC ic;
D(IC ic) {
this.ic = ic;
ic.printMess();
}
}
/测试类
public class Test {
public static void main(String[] args) {
new D(new A());
new D(new B());
}
}
输出结果:
我的信息是A
我的信息是B
通过D类进行接口依赖注入,如果新增类去答应信息只需要在Test类中动态配置注入的参数就行。
你肯定会说,这三种依赖注入的方式已经解决了程序解耦的问题,为什么还会出来一个Dagger2呢?那是因为上面3中依赖注入的方式都不能解决一个问题,那就是面对A、B类在程序中大量并且多个地方创建我们需要大量的new操作,如果这个时候有一个需求需要改变构造参数你该怎么办呢?全局搜索一个个去改?刚改完需求又变动怎么办?好吧我承认我受不了......
那么我们的Dagger2就横空出世了,它通过java的注解的方式创建对象,省去new操作,在修改构造参数的时候只需要在一处修改即可,下面我们接着看如何实现。
public class DaggerBean {
private int age;
private String name;
@Inject
public DaggerBean() {
age = 17;
name = "张三";
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
}
public class DaggerActivity extends AppCompatActivity {
@Inject
DaggerBean bean;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
initView();
}
private void initView() {
TextView content = (TextView) findViewById(R.id.tv_dagger);
content.setText(bean.getName()+"今年"+bean.getAge()+"岁了");
}
}
直接运行程序会发生空指针异常,因为直接通过@Inject 注入DaggerBean对象是不会成功的,因为DaggerActivity不知道去哪里找到它的实例去注入生成,这时我们需要一个连接器Component,让上面这两个类产生联系:
//创建一个Component
/**
* 1.必须是接口
* 2.必须使用Component注解
*/
@Component
public interface DaggerComponent {
/**
* inject名称可以更改
* @param daggerActivity 将Activity对象和DaggerBean进行关联的方法
*/
void inject(DaggerActivity daggerActivity);
}
build一下项目(必须先build),然后我们修改一下DaggerActivity中的代码
public class DaggerActivity extends AppCompatActivity {
@Inject
DaggerBean bean;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
initView();
}
private void initView() {
//dagger2通过APT技术生成的类(后续我们会有专门的篇幅分析源码),进行注入
DaggerDaggerComponent.builder()
.build()
.inject(this);
TextView content = (TextView) findViewById(R.id.tv_dagger);
content.setText(bean.getName() + "今年" + bean.getAge() + "岁了");
}
}
运行结果

这一次运行成功了,是不是so easy!!!
@Module
尽管上面我们实现了操作但是同时我们也发现了一些问题:
- 在DaggerBean的构造函数需要使用@Inject进行注解,如果DaggerBean是系统类或者第三方库的类我们怎么办?
- 同时使用@Module和@Inject会以哪一个为主?
- 类的构造函数带有参数,直接使用@Inject会出现编译错误怎么办?
为了解决上面的疑惑我们的下一个主角@Module闪亮登场,@Module和@Inject属于两个不同的纬度,我们先看第1个问题,首先先创建一个DaggerModule
/**
* 1.可以提供一些对象实例
* 2.提供构造函数所需要的参数
*/
@Module
public class DaggerModule {
/**
* 必须使用Provides进行注解
* @return 提供一个第三方库对象
*/
@Provides
public Gson provideGson(){
return new Gson();
}
}
修改一下DaggerActivity
public class DaggerActivity extends AppCompatActivity {
@Inject
DaggerBean bean;
@Inject
Gson gson;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
initView();
}
private void initView() {
//dagger2通过APT技术生成的类(后续我们会有专门的篇幅分析源码),进行注入
DaggerDaggerComponnet.builder()
.build()
.inject(this);
TextView content = (TextView) findViewById(R.id.tv_dagger);
content.setText(gson.toJson(bean));
//content.setText(bean.getName() + "今年" + bean.getAge() + "岁了");
}
}
运行项目显示结果

接下来看第2个问题,首先修改DaggerBean
public class DaggerBean {
private int age;
private String name;
@Inject
public DaggerBean() {
age = 17;
name = "张三";
}
public DaggerBean(int age,String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
}
修改
/**
* 1.可以提供一些对象实例
* 2.提供构造函数所需要的参数
*/
@Module
public class DaggerModule {
/**
* 必须使用Provides进行注解
* @return 提供一个第三方库对象
*/
@Provides
public Gson provideGson(){
return new Gson();
}
@Provides
public DaggerBean provideDaggerBean(){
return new DaggerBean(20,"李四");
}
}
运行项目

从运行结果可以看出来,module纬度的优先级更高一些。
第三个问题特别需要注意一下,先看一下构造函数
@Inject
public DaggerBean(int age,String name) {
this.age = age;
this.name = name;
}
如果我们直接把代码写成这样去编译使通不过的,因为我们没有给构造函数传参数,并且构造函数中如果不能使用基本数据类型去传参,下面看一下如果我们想传递构造函数的参数如何来使用:
新增一个类WrapDaggerData,进行包装数据
public class WrapDaggerData {
public int getAge(){
return 22;
}
public String getName(){
return "王五";
}
}
//更改一下构造参数
public class DaggerBean {
private int age ;
private String name ;
@Inject
public DaggerBean(WrapDaggerData wrapDaggerData) {
age = wrapDaggerData.getAge();
name = wrapDaggerData.getName();
}
}
//修改一下
@Module
public class DaggerModule {
/**
* 必须使用Provides进行注解
* @return 提供一个第三方库对象
*/
@Provides
public Gson provideGson(){
return new Gson();
}
/**
* 这里是提供构造参数的地方
* @return
*/
@Provides
public WrapDaggerData provideDaggerGsonData(){
return new WrapDaggerData();
}
}
//修改一下
@Component(modules = DaggerModule.class)
public interface DaggerComponnet {
void inject(DaggerActivity daggerActivity);
/**
*这里是重点,需要什么类型的构造参数需要在这里定义,方法名称没有特殊限制,这里是链接构造参数的桥梁
* @return
*/
WrapDaggerData getDaggerGsonData();
}
运行项目

从结果可以看出来构造函数的值传递成功。
好了这篇文章就写到这里结束,这篇文章主要是介绍module和inject二种纬度的使用方法和区别以及构造函数如何传参的问题。
如有写的不对的地方敬请留言告知!
网友评论