Dagger2从入门到熟练
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从入门到熟练相关推荐
- linux内核二当家,Linux PWN从入门到熟练(二)
前言 上回说到,如何利用程序中system函数以及bin/sh字符串来进行pwn.这里我们会介绍,如何在栈可执行而system函数以及参数没有的情况下,如何自己布置payload进行pwn.此外,还提 ...
- 【github干货】主流深度学习开源框架从入门到熟练
文章首发于微信公众号<有三AI> [github干货]主流深度学习开源框架从入门到熟练 今天送上有三AI学院第一个github项目 01项目背景 目前深度学习框架呈百家争鸣之态势,光是为人 ...
- matlab z变换离散化_MATLAB作图从入门到熟练
有同学说,靠网络上的文章,很难学到系统的知识,还得自己看书,这话不假.主要是因为网上文章篇幅过短,难免无法概括全面,加之同学们更关心一些高效的学习方法,更倾向于接受高密集信息的学习方式,节省时间和精力 ...
- Linux PWN从入门到熟练
最近在复习pwn的一些知识.主要涉及到当堆栈开启了保护的时候,我们不能够直接将shellcode覆盖到堆栈中执行,而需要利用程序其他部分的可执行的小片段来连接成最终的shellcode.此小片段就是g ...
- RESTful从入门到熟练,看完这篇就够了
学习要求 良好的java基础, 熟悉SpringBoot框架,熟悉SpringMVC框架 教学目标 掌握RESTful接口设计 视频教程 5小时带你入门到熟练RESTful接口设计 铺垫 先说明,本篇 ...
- .Net Redis 入门到熟练
内存缓存主要有两种,一种是应用服务自己的memcache,类似于 webapi的session 其实都是服务自己的应用级内存,另外一种是分布式的缓存服务,此服务在另外的机器上,耗费的都是此机器的内存资 ...
- 大数据技术之Hadoop分布式文件系统HDFS系统知识整理(从入门到熟练操作)
系列博客 1.大数据技术之Hadoop完全分布式集群搭建+Centos7配置连通外网和主机 2.大数据技术之Hadoop编译源码 3.大数据技术之Hadoop分布式文件系统HDFS系统知识整理(从入门 ...
- python的django框架从入门到熟练【保姆式教学】第一篇
当今,Python已成为最受欢迎的编程语言之一.而Django是一个基于Python的Web框架,它能够帮助你快速.高效地开发Web应用程序.如果你是一名初学者,学习Django框架可能会让你感到有些 ...
- 【软件开发底层知识修炼】十九 GDB调试从入门到熟练掌握超级详细实战教程学习目录
本文记录之前写过的5篇关于GDB快速学习的文章,从第一篇开始学习到最后一篇,保证可以从入门GDB调试到熟练掌握GDB调试的技巧. 学习交流加 个人qq: 1126137994 个人微信: liu112 ...
最新文章
- emoji 乱码_这个自制emoji的网站,让你成为永远不输的斗图王者
- 以前初学php用的分页函数
- java.util.date_关于java中java.util.Date(急)
- pear Auth的使用
- WCF入门到精通(二)——契约
- Java线程面试的前50个问题,面向初学者和经验丰富的程序员
- NSURLSessionDownloadTask实现大文件下载
- 一下删除MSSQL表所有的数据,但不删除表结构
- VMware Vsphere 6.0安装部署 总体部署架构
- Windows Server 2003下ASP.NET无法识别IE11的解决方法【转】
- 使用定位技术,边界判断要谨慎
- Adam自适应矩估计
- C语言递归方法求解背包问题
- python EXCEL表格数据对比
- The TARGETDIR variable must be provided when invoking this installer的解决方案
- Linux中的PS1变量
- navicat12No All Pattern Found!File Already Patched。
- python编程自然数表达式_实现四则运算 (python实现)by 周乃君 张宏根
- 4k纸是几厘米乘几厘米_4k纸有多大?长宽各多少厘米?
- 手机开启自动调节亮度,到底是省电还是耗电?为何?