Dagger2是什么?

是一个依赖注入框架,butterknife也是一个依赖注入框架。不过butterknife,最多叫奶油刀,Dagger2被叫做利器啊,他的主要作用,就是对象的管理,无需显示调用new Object(),其目的是为了降低程序耦合。

来看一个例子:

public class A {public void eat() {System.out.print("吃饭了");}
}

使用的时候我们就要

A a = new A();
a.eat();

如果现在改了,在A的构造方法中必须传入B对象

 public class A {private B b;public A(B b) {this.b = b;}public void eat() {System.out.print("吃饭了");}
}

那么使用的时候

A a = new A(new B());
a.eat();

可能就有人说了,不就加一个对象么,这里只是我举的一个很简单的例子,看起感觉很简单,但是在实际开发中,如果现在改了一个这个构造方法。是不是意味着,整个项目中的都的改,一不小心, 就是BUG 。

因此,为了避免这种动不动就要传一个对象进入的情况,出现了dagger。看到这篇文章的同学,多少都有点了解dagger了。还是直接介绍怎么使用吧。使用之前,先开局一张图:

简单说一下几个概念,有个认知,在往下看,会好很多,Dagger 是通过@Inject使用具体的某个对象,这个对象呢,是由@Provides注解提供,但是,这个@Provides只能在固定的模块中,也就是@Module注解,我们查找的时候,不是直接去找模块,而是去找@Component。

@Inject
A a

想要获取a对象的示例的时候,Dagger2 会先去找,当前Activity或者Fragment所连接的桥梁,例如上图中,连接的只有一个桥梁,实际上可以有多个,这个桥梁,会去寻找他所依赖的模块,如图中,依赖了模块A,和模块B,然后在模块中,会去寻找@Providers注解,去寻找A的实例化对象。

1.@Inject

  • 提供实例的类的是默认的构造函数

1. @Inject 注解在一个 Filed 域. 表示在 MainActivity类中需要注入一个 Engine 对象

public class MainActivity extends AppCompatActivity {// 这个 @Inject 表示要注入一个 Engine对象@Inject Engine mEngine;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);DaggerMainComponent.create().inject(this);}public void onClick(View view) {Toast.makeText(this,mEngine.toString(),Toast.LENGTH_SHORT).show();}
}

2. @Inject 注解在一个构造函数中,表示可以提供该类的实例供注入别的类中 ,有且只有一个Engine 的构造函数能被 @Inject 标注

public class Engine {private String model;private int age;// 提供 Engine 对象@Injectpublic Engine() {model = "xxxx";age = 1990;}public Engine(String model, int age) {this.model = model;this.age = age;}@Overridepublic String toString() {return "Engine{" +"model='" + model + '\'' +", age=" + age +'}';}
}

3. 定义一个接口并用 @Component 标注

用来把 Engine 注入到 MainActivity 中

@Component
public interface MainComponent {/*** 必须让Component知道需要往哪个类中注入,这个方法名可以是其它的,但是推荐用inject* 目标类MainActivity必须精确,不能用它的父类*/void inject(MainActivity activity);
}

4. 在 MainActivity 中 onCreate 方法中调用

// DaggerMainComponent 这个类是 Dagger 框架自动生成的辅助类,
DaggerMainComponent.create().inject(this);

之后就可以使用 mEngine 这个对象了,框架是通过调用 Engine 的构造函数生成了一个 Engine 对象 并注入了 MainActivity。

  • 注入的对象构造函数有参数,也需要提供参数对象

public class MainActivity extends AppCompatActivity {@Inject Car mCar;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);DaggerMainComponent.create().inject(this);}public void onClick(View view) {Toast.makeText(this,mCar.toString(),Toast.LENGTH_SHORT).show();}
}

dagger 框架会直接通过 Engine Tire的被 @Inject 的构造函数提供 Car 构造函数的参数对象。

public class Car {private Engine mEngine;private Tire mTire;@Injectpublic Car(Engine engine,Tire tire) {mEngine = engine;mTire = tire;}@Overridepublic String toString() {return "Car{" +"mEngine=" + mEngine +", mTire='" + mTire + '\'' +'}';}
}

Engine对象见 前文2处;

public class Tire {private int radius;// 提供 Car 构造函数的 Tire 对象@Injectpublic Tire() {radius = 80;}@Overridepublic String toString() {return "Tire{" +"radius=" + radius +'}';}
}

@Inject无法满足所有的要求 :

  • 接口无法被构造.
  • 引用的第三方框架对象无法被注解.
  • 可配置的对象无法被配置,只能使用构造函数构造

2. 使用 @Module @Provides

@Inject的局限性, Dagger 提供了另外一套提供实例的办法

@Module
public class TireModule {//可以提供一个 Tire 对象实例@Providespublic static Tire provideTire(){return new Tire();}
}
@Module
public class EngineModule {// 可以提供一个 Engine 对象实例@Providespublic static Engine provideEngine(){return new Engine("F1234",2018);}
}
@Component(modules = {TireModule.class,EngineModule.class})
public interface MainComponent {void inject(MainActivity activity);
}

当要注入 Car 现在构造函数有两个地方可以提供 Engine / Tire对象作为 Car 构造函数的参数

  • @Inject 标注的构造函数提供
  • @Module @Provides 提供

Dagger 优先使用 @Module @Provides 提供的对象.

3. @Scope:作用域 并不使用在类中,用于定义注解

@Scope的作用主要是在组织Component和Module的时候起到一个实例作用范围的提醒(类似是生命周期)

  • 已经定义好的作用域 @Singleton

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

@Provides 加上 @Singleton 表明会使用相同的 Car 对象去初始化所有需要 Car 对象的客户端:

@Module
public class CarModule {@Provides@Singletonpublic static Car provideCar1(){return new Car(new Engine("F-7890",2020),new Tire());}
}

Component的也必须标注 @Singleton 不然会编译报错,作用域不匹配:

@Component(modules = CarModule.class)
@Singleton
public interface MainComponent {Car maker();
}
public class MainActivity extends AppCompatActivity {private Car car1;private Car car2;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MainComponent component = DaggerMainComponent.create();car1=component.maker();car2=component.maker();}public void onClick(View view) {Toast.makeText(this," car1==car2 : "+(car1==car2),Toast.LENGTH_SHORT).show();}
}
car1 = car : true

若去掉 @Singleton 注解结果为 false。

@Singleton并不是我们传统单例模式的那种作用,只能保证在一个Component当中只提供同一个实例对象 但并不是整个的应用全局只有一个对象,调用DaggerMainComponent.create()两次产生两个 MainComponent对象也会产生两个Car对象,要保证应用全局单例可以配合android中application的特殊性,就可以实现应用全局单例

Module中的方法 使用@Singleton标注后,那对应的Component也必须采用@Singleton标注,表明它们的作用域一致,否则编译的时候会报作用域不同的错误。

  • 自定义作用域

@ActivityScoped是一个自定义的作用域注解,作用是允许对象被记录在正确的组件Activity中,当然这些对象的生命周期应该遵循activity的生命周期

@Documented
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScoped {
}

@FragmentScoped是一个自定义的作用域注解,作用是允许对象被记录在正确的组件Fragment中,当然这些对象的生命周期应该遵循Fragment的生命周期

@Scope
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface FragmentScoped {}

4. @Qualifier:限定符 并不使用在类中,用于定义注解

  • 定义好的限定符 @Named

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {/** The name. */String value() default "";
}
 @Named("provideCar2")@Inject Car mCar;

在宿主中使用时,可以通过 @Named 来选择所需要的那个实例

@Module
public class CarModule {@Providespublic static Car provideCar1(){return new Car(new Engine("F-7890",2020),new Tire());}@Provides@Named("provideCar2")public static Car provideCar2(){return new Car(new Engine("X-7890",2030),new Tire());}
}
@Component(modules = CarModule.class)
public interface MainComponent {void inject(MainActivity activity);
}
  • 自定义限定符

@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface ForTest {
}

5. @Binds

@Binds 可以用于当需要一个父类对象时,用子类对象进行替代 (接口同理)

public class Car {}public class BenzCar extends Car {@Injectpublic BenzCar() {}
}
@Module
public abstract class CarModule {@Bindsabstract Car provideCar(BenzCar car);
}
@Component(modules = CarModule.class)
public interface MainComponent {Car maker();
}
public class MainActivity extends AppCompatActivity {@InjectCar car;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);car=DaggerMainComponent.create().maker();}public void onClick(View view) {Toast.makeText(this,car.getClass().getSimpleName(),Toast.LENGTH_SHORT).show();}}

结果为 :

BenzCar

通过 @Binds 当需要注入一个 Car 对象 可以使用子类对象 BenzCar 进行绑定注入.

6. @BindsInstance

上述注入对象都是通过 Dagger 自动注入的,如果我们需要 new 一个对象传入 Dagger 就可以使用 @BindsInstance

public class Car {private Engine mEngine;private Tire mTire;// 提供一个 Car 对象,但是需要 Engine / Tire对象作为参数@Inject public Car( Engine engine, Tire tire) {mEngine = engine;mTire = tire;}}
public class Engine {private String model;private int age;public Engine(String model, int age) {this.model = model;this.age = age;}
}
public class Tire {private int radius;// Tire 不提供对象,手动注入public Tire(int radius) {this.radius = radius;}
}

@Component.Builder 注明一个 MainComponent builder @BindsInstance 定义一个传入手动对象的方法

@Component(modules = EngineModule.class)
public interface MainComponent {Car maker();@Component.Builderinterface Builder {@BindsInstanceBuilder tire(Tire tire);MainComponent build();}
}

要构造 Car 对象需要 Engine Tire对象 ,Engine对象框架自动生成, Tire对象由我们手动传入

public class MainActivity extends AppCompatActivity {@InjectCar car;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 手动注入 Tire 对象car=DaggerMainComponent.builder().tire(new Tire(90)).build().maker();}public void onClick(View view) {Toast.makeText(this,car.toString(),Toast.LENGTH_SHORT).show();}}

7. Lazy<T> 和 Provider<T>

  • Lazy<T>
@Module
public class CarModule {@Providespublic static Car provideCar1(){return new Car(new Engine("F-7890",2020),new Tire());}}
@Component(modules = CarModule.class)
public interface MainComponent {Lazy<Car> maker();
}
public class MainActivity extends AppCompatActivity {private Car car1;private Car car2;@InjectLazy<Car> mCarLazy;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mCarLazy=DaggerMainComponent.create().maker();car1=mCarLazy.get();car2=mCarLazy.get();}public void onClick(View view) {Toast.makeText(this," car1==car2 : "+(car1==car2),Toast.LENGTH_SHORT).show();}}

car1 = car : true

当注入 Lazy<Car>对象的时候 只有在调用 get 方法的时候才会去调用 provideCar1 初始化 Car 对象,实现懒加载 且会缓存 Car 对象 每次进行 get()的时候返回的是同一个 Car 对象.

  • Provider<T>

**把上述的代码的 Lazy<Car> 改为 Provider<Car>,其他不变 **

car1 = car : false

当注入 Provider<Car> 对象的时候 每次调用 get 方法的时候才会去调用 provideCar1去创建一次新的 Car 对象.

如果 Provider<Car> 注入过程中给 @Provides ,@Component 加上 @Singleton结果会如何呢 ?

@Module
public class CarModule {@Provides@Singletonpublic static Car provideCar1(){return new Car(new Engine("F-7890",2020),new Tire());}}@Component(modules = CarModule.class)
@Singleton
public interface MainComponent {Provider<Car> maker();
}

结果为 :

car1 = car : true

当注入 Provider<Car> 对象的时候 每次调用 get 方法的时候才会去调用 provideCar1去创建 Car 对象,但是 provideCar1已经被 @Singleton ,所以不管怎样,provideCar1只会返回相同的Car 对象.

Dagger2从入门到熟练相关推荐

  1. linux内核二当家,Linux PWN从入门到熟练(二)

    前言 上回说到,如何利用程序中system函数以及bin/sh字符串来进行pwn.这里我们会介绍,如何在栈可执行而system函数以及参数没有的情况下,如何自己布置payload进行pwn.此外,还提 ...

  2. 【github干货】主流深度学习开源框架从入门到熟练

    文章首发于微信公众号<有三AI> [github干货]主流深度学习开源框架从入门到熟练 今天送上有三AI学院第一个github项目 01项目背景 目前深度学习框架呈百家争鸣之态势,光是为人 ...

  3. matlab z变换离散化_MATLAB作图从入门到熟练

    有同学说,靠网络上的文章,很难学到系统的知识,还得自己看书,这话不假.主要是因为网上文章篇幅过短,难免无法概括全面,加之同学们更关心一些高效的学习方法,更倾向于接受高密集信息的学习方式,节省时间和精力 ...

  4. Linux PWN从入门到熟练

    最近在复习pwn的一些知识.主要涉及到当堆栈开启了保护的时候,我们不能够直接将shellcode覆盖到堆栈中执行,而需要利用程序其他部分的可执行的小片段来连接成最终的shellcode.此小片段就是g ...

  5. RESTful从入门到熟练,看完这篇就够了

    学习要求 良好的java基础, 熟悉SpringBoot框架,熟悉SpringMVC框架 教学目标 掌握RESTful接口设计 视频教程 5小时带你入门到熟练RESTful接口设计 铺垫 先说明,本篇 ...

  6. .Net Redis 入门到熟练

    内存缓存主要有两种,一种是应用服务自己的memcache,类似于 webapi的session 其实都是服务自己的应用级内存,另外一种是分布式的缓存服务,此服务在另外的机器上,耗费的都是此机器的内存资 ...

  7. 大数据技术之Hadoop分布式文件系统HDFS系统知识整理(从入门到熟练操作)

    系列博客 1.大数据技术之Hadoop完全分布式集群搭建+Centos7配置连通外网和主机 2.大数据技术之Hadoop编译源码 3.大数据技术之Hadoop分布式文件系统HDFS系统知识整理(从入门 ...

  8. python的django框架从入门到熟练【保姆式教学】第一篇

    当今,Python已成为最受欢迎的编程语言之一.而Django是一个基于Python的Web框架,它能够帮助你快速.高效地开发Web应用程序.如果你是一名初学者,学习Django框架可能会让你感到有些 ...

  9. 【软件开发底层知识修炼】十九 GDB调试从入门到熟练掌握超级详细实战教程学习目录

    本文记录之前写过的5篇关于GDB快速学习的文章,从第一篇开始学习到最后一篇,保证可以从入门GDB调试到熟练掌握GDB调试的技巧. 学习交流加 个人qq: 1126137994 个人微信: liu112 ...

最新文章

  1. emoji 乱码_这个自制emoji的网站,让你成为永远不输的斗图王者
  2. 以前初学php用的分页函数
  3. java.util.date_关于java中java.util.Date(急)
  4. pear Auth的使用
  5. WCF入门到精通(二)——契约
  6. Java线程面试的前50个问题,面向初学者和经验丰富的程序员
  7. NSURLSessionDownloadTask实现大文件下载
  8. 一下删除MSSQL表所有的数据,但不删除表结构
  9. VMware Vsphere 6.0安装部署 总体部署架构
  10. Windows Server 2003下ASP.NET无法识别IE11的解决方法【转】
  11. 使用定位技术,边界判断要谨慎
  12. Adam自适应矩估计
  13. C语言递归方法求解背包问题
  14. python EXCEL表格数据对比
  15. The TARGETDIR variable must be provided when invoking this installer的解决方案
  16. Linux中的PS1变量
  17. navicat12No All Pattern Found!File Already Patched。
  18. python编程自然数表达式_实现四则运算 (python实现)by 周乃君 张宏根
  19. 4k纸是几厘米乘几厘米_4k纸有多大?长宽各多少厘米?
  20. 手机开启自动调节亮度,到底是省电还是耗电?为何?

热门文章

  1. linux ubuntu基础,linux基础入门详细分析(基于ubuntu)
  2. 实现MFC中Radio Button组绑定同一变量控制
  3. mssql 查询当前自增序号_查询函数Choose、Lookup、Hlookup、Vlookup应用技巧解读
  4. PE Header中的FIleHeader(文件头)
  5. javascript中将整数添加千位符号
  6. 记录memcache分布式策略及算法
  7. ubuntu时钟不显示的解决方法
  8. 多线程基础(五)NSThread线程通信
  9. JAVA中定义常量的几种方式
  10. 文件目录Android SDK目录结构