目录
- 一:Dagger2是什么?
- 二:为什么要有Dagger2
- 三: Dagger2的原理
- 四: Dagger2的优点
- 五:Dagger2如何使用
-
-
- 基本的概念
-
- 如何使用Dagger2
-
- 高级用法
- (1)构造方法需要其他参数时候
- (2) 模块之间的依赖关系
- (3) @Named注解使用
- (4) @Singleton注解
- (5)自定义Scoped
- (6)Subcomponent
- (7)lazy 和 Provider
-
- 六: MVP + Dagger2
- 七: 注意的问题和踩过的坑
作者:Allens_Jiang
链接:https://www.jianshu.com/p/2cd491f0da01
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
MVP + Dagger2
一:Dagger2是什么?
是一个依赖注入框架,butterknife也是一个依赖注入框架。不过butterknife,最多叫奶油刀,Dagger2被叫做利器啊,他的主要作用,就是对象的管理
dagger的用途就是:让你**不需要初始化对象。**换句话说,任何对象声明完了就能直接用。
优点:
其目的是为了降低程序耦合。
随即而来的就是可测试性,可维护,可扩展性就大大提高了
很多小白对于Dagger2是啥浑然不知,更不知其能带来的好处了。这里举个例子,比如有个类A,他的构造函数需要传入B,C;然后代码里有10个地方实例化了A,那如果功能更改,A的构造函数改成了只有B,这个时候,你是不是要去这10个地方一个一个的改?如果是100个地方,你是不是要吐血?!如果采用dagger2,这样的需求只需要改1-2个地方,你感觉怎么样?对你有诱惑吗?
原理:
这类依赖注入框架都已经采用了apt代码自动生成技术,其注解是停留在编译时,完全不影响性能。
dagger是使用依赖注入的方式,使用Annotation给需要注入的对象做标记,通过inject()方法自动注入所有对象,从而完成自动的初始化
dagger2 解决什么问题
第一:dagger 是一个依赖注入框架,首要任务当然是解决依赖注入的问题。
第二:dagger主要想通过编译时产生代码的方式来解决那些基于反射的依赖注入框架所存在的缺点,例如性能问题,开发过程中存在的问题。
注入的形式:
public class MainActivity extends AppCompatActivity { @Named("dev") @Inject MainApi apiDev; @Named("release") @Inject MainApi apiRelease; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainComponent.builder() .mainModule(new MainModule(this)) .mainChildModule(new MainChildModule()) .build() .inject(this); apiDev.eat(); apiRelease.eat(); Log.i("TAG","apiDev--->" + apiDev); Log.i("TAG","apiRelease--->" + apiRelease); }
3要素:Inject Component Module
@Inject
在A类中标记B类的字段:告诉dagger 我们要注入B类的实例到A类中,你帮我搞定。
在B类中标记B类的构造函数:告诉dagger B类是一个可以被注入的类,如果你想把B的实例对象注入到其他类中,例如注入到A类中,是没有任何问题的。
@Component :中间桥接,对外接口
一般是一个使用 @Component标记的接口,其持有A类的实例,其在A类中发现有使用@Inject
标记的属性b
@Module和 providers绑定在一起
C类是一个没有使用@Inject
注解其构造函数的类
@Module注解表示,这个类是一个Module,Module的作用是提供信息,让ObjectGraph知道应该怎样注入所有的依赖
例子:
1.c 类
public class C { public void turnDown(){ System.out.println("癞蛤蟆想吃天鹅肉,滚!"); } }
a类:需要依赖b类和吃c类
B类和c类一样,单独的
2.定义Module
@Module public class DaggerModule { //此处为了尽量简单,其实这边可以有很多种写法 @Provides public C providerC(){ return new C(); } }
定义Component
就是一个使用@Component标记的一个接口,把这个Component 要用到的Module以modules = {Module1.class,Module2.class}的形式提供出来,例如下面的代码中,MatchComponent 依赖了一个DaggerModule。 @Component(modules = {DaggerModule.class}) public interface MatchComponent { void mainActivityInject(MainActivity activity); }
运用:
public class MainActivity extends AppCompatActivity { @Inject A a; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MatchComponent component= DaggerMatchComponent .builder() .build(); component.mainActivityInject(this); findViewById(R.id.btn_niu).setOnClickListener(v->{a.xxoo();}); findViewById(R.id.btn_xue).setOnClickListener(v->{a.appointment();}); } }
原理分析:
1.a是怎么实例化的
2.b和c是怎么是咧化的
3.他们是怎么绑定的
原理:在build里面生成了很多的类,工厂模式,代理模式
A_Factory ,proxyProviderC
1、最简单不带 Module 的 Inject 方式;
2、带 Module 的 Inject 方式;
3、Component依赖Component
1、最简单不带Module的Inject方式
由我们自己定义的类,我们可以自由修改的情况下我们使用这种方式,也分为两种:带参数和不带参数的。
a、构造参数不带参数的:
例子:
b、构造参数带参数的:
构造函数如果带了inject注入的话,类就默认被注入了,不用像无惨的一样
例子:
发现Factory的构造函数被@Inject标注了且带有一个参数,
然后dagger2就去寻找Product发现它的构造函数也被@Inject标注并且无参数,于是dagger2把Product的实例注入给FactoryActivity
2、带Module的Inject方式
应用场景:若是我们引入的第三方库不能随意改动代码的话就不方便了,我们这里使用如下RetrofitManager模拟不可改动代码的情况:
- 第三方库提供的类,它们的构造方法不能被注解
对于这样的情况,可以使用@Provides注解来提供专用的初始化方法,实现自定义依赖。
@Provides
Coder provideCoder(Boss boss) {
return new Coder(boss);
}
Rebuild Project,然后AS 会自动帮我们生成一个Component
include的用法
@Module (includes = {BModule.class})// includes 引入) public class AModule { @Provides A providerA() { return new A(); } }
@Named注解用
相当于有个表示,虽然大家都是同一个对象,但是实例化对象不同就不如
A a1 = new A();
A a2 = new A();
// a1 a2 能一样嘛
Module中 使用@Named
注解
@Module public class MainModule { private MainActivity activity; public MainModule(MainActivity activity) { this.activity = activity; } @Named("dev") @Provides MainApi provideMainApiDev(MainChildApi mainChildApi, String url) { return new MainApi(mainChildApi, activity,"dev"); } @Named("release") @Provides MainApi provideMainApiRelease(MainChildApi mainChildApi, String url) { return new MainApi(mainChildApi, activity,"release"); } }
来源: https://www.jianshu.com/p/2cd491f0da01
在Activity/Fragment中使用
public class MainActivity extends AppCompatActivity { @Named("dev") @Inject MainApi apiDev; @Named("release") @Inject MainApi apiRelease; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainComponent.builder() .mainModule(new MainModule(this)) .mainChildModule(new MainChildModule()) .build() .inject(this); apiDev.eat(); apiRelease.eat(); Log.i("TAG","apiDev--->" + apiDev); Log.i("TAG","apiRelease--->" + apiRelease); } }
在项目中的应用场景和实例:
https://blog.csdn.net/soslinken/article/details/52184113
注意:
实质上,Dagger会在编译时对代码进行检查,并在检查不通过的时候报编译错误(为什么?这和Dagger的原理有关,有兴趣的话可以关注我之后发布的Dagger详解)。检查内容主要有三点:
- 所有含有依赖注入的类,需要被显式 声明在相应的Module中。
- 一个Module中所有@Provides方法的参数都必须在这个Module种提供相应的@Provides方法,或者在@Module注解后添加“complete = false”注明这是一个不完整Module(即它会被其他Module所扩展)。
- 一个Module中所有的@Provides方法都要被它声明的注入对象所使用,或者在@Module注解后添加“library = ture”注明(即它是为了扩展其他Module而存在的)。
参考博客:
https://blog.csdn.net/shusheng0007/article/details/80950117