dagger2使用方法教程之简明讲解
前言
Dagger这个库的取名不仅仅来自它的本意“匕首”,同时也暗示了它的原理。JakeWharton在对Dagger的介绍中指出,Dagger即DAG-er,这里的DAG即数据结构中的DAG——有向无环图(DirectedAcyclicGraph)。也就是说,Dagger是一个基于有向无环图结构的依赖注入库,因此Dagger的使用过程中不能出现循环依赖。
Android开发从一开始的MVC框架,到MVP,到MVVM,不断变化。现在MVVM的data-binding还在实验阶段,传统的MVC框架Activity内部可能包含大量的代码,难以维护,现在主流的架构还是使用MVP(Model+View+Presenter)的方式。但是MVP框架也有可能在Presenter中集中大量的代码,引入DI框架Dagger2可以实现Presenter与Activity之间的解耦,Presenter和其它业务逻辑之间的解耦,提高模块化和可维护性。
现在的公司项目用到了Dagger2,之前只是稍微了解一些,没有用过,然后查了查资料,整理如下,方便快速上手
四个基本注解
1、@Inject主要有两个作用,一个是使用在构造函数上,通过标记构造函数让Dagger2来使用(Dagger2通过Inject标记可以在需要这个类实例的时候来找到这个构造函数并把相关实例new出来)从而提供依赖,另一个作用就是标记在需要依赖的变量让Dagger2为其提供依赖。
@Inject注解的字段不能是private和protected的
2、@Module用Module标注的类是专门用来提供依赖的。有的人可能有些疑惑,看了上面的@Inject,需要在构造函数上标记才能提供依赖,那么如果我们需要提供的类构造函数无法修改怎么办,比如一些jar包里的类,我们无法修改源码。这时候就需要使用Module了。Module可以给不能修改源码的类提供依赖,当然,能用Inject标注的通过Module也可以提供依赖。
这里需要注意,Module和Inject这两个注解还是有区别的,@Inject使用在构造函数上的时候,这个构造函数有没有参数都可以,如果有参数的话这个Module也需要有其他Module或者@Inject构造函数提供实例,适合在提供该类自己的时候使用。但是如果用@Module的话,@Module注解的这个类需要有默认无参构造函数(显示隐式都可以),否则会报“”xxxmustbeset”。如果没有默认无参构造函数,就需要手动把这个Module的实例传入Component,一般在MVP模式里使用该方式,用来提供Activity实例给Presenter实例。
所以,如果该类只需要提供自己,建议直接使用@Inject函数,如果是用来提供其他类的实例,建议使用@Module的方式。
3、@Provides用Provides来标注一个方法,该方法可以在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了@Inject的变量赋值。provides主要用于标注Module里的方法。
4、@Component一般用来标注接口,被标注了Component的接口在编译时会产生相应的类的实例来作为提供依赖方和需要依赖方之间的桥梁,把相关依赖注入到其中。
四个扩展注解
1、@Qulifier这里有个概念,叫依赖迷失,就是在Module注解的类里,有2个Provides都提供某个类的实例,这时候不用@Qulifier注解的话Component会不知道用哪个实例,这时候就要使用@Qulifier,下面直接提供代码
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public@interfaceA{}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public@interfaceB{}
@Module
publicclassSimpleModule{
@Provides
@A
CookerprovideCookerA(){
returnnewCooker("James","Espresso");
}
@Provides
@B
CookerprovideCookerB(){
returnnewCooker("Karry","Machiato");
}
}
publicclassComplexMakerimplementsCoffeeMaker{
CookercookerA;
CookercookerB;
@Inject
publicComplexMaker(@ACookercookerA,@BCookercookerB){
this.cookerA=cookerA;
this.cookerB=cookerB;
}
}
2、@Named和@Qulifier一样,并且@Named就是继承@Qulifier的,而且用起来比@Qulifier方便,示例代码如下:
@Module
publicclassMainModule{
@Provides
@Named("red")
publicClothgetRedCloth(){
Clothcloth=newCloth();
cloth.setColor("红色");
returncloth;
}
@Provides
@Named("blue")
publicClothgetBlueCloth(){
Clothcloth=newCloth();
cloth.setColor("蓝色");
returncloth;
}
@Provides
publicClothesgetClothes(@Named("blue")Clothcloth){
returnnewClothes(cloth);
}
}
publicclassMainActivityextendsAppCompatActivity{
...
@Inject
@Named("red")
ClothredCloth;
@Inject
@Named("blue")
ClothblueCloth;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
...
tv.setText("我现在有"+redCloth+"和"+blueCloth);
}
}
3
//PerActivity.java
@Scope
@Retention(RetentionPolicy.RUNTIME)
public@interfacePerActivity{}
//ActivityModule.java
@Module
publicclassActivityModule{
@Provides
CoffeeShopprovideCoffeeShop(){
returnCoffeeShop.getInstance();//一个普通的单例
}
/**
*直接在这里说结果,@PerActivity是用@Scope注解的,除了在这里注解还需要在用到该Module类的Component的类名上方也要注解,然后该实例在注入到某个类里的时候用同一个Component就会不管有几个字段都会只有一个实例。注意:如果用不同的Component实例的话仍然会新的CookerFactory实例,单例CookerFactory只存在一个Component实例里。所以叫局部单例。
*/
@Provides
@PerActivity
CookerFactoryprovideCookerFactory(){
returnnewCookerFactory();
}
@Provides
CookerFactoryMultyprovideCookerFactoryMulty(){
returnnewCookerFactoryMulty();//非单例
}
}
//CoffeeShop.java
publicclassCoffeeShop{
privatestaticCoffeeShopINSTANCE;
privateCoffeeShop(){
Log.d("TAG","CoffeeShopNewInstance");
}
publicstaticCoffeeShopgetInstance(){
if(INSTANCE==null){
INSTANCE=newCoffeeShop();
}
returnINSTANCE;
}
}
//CookerFactory.java
publicclassCookerFactory{
publicCookerFactory(){
Log.d("TAG","CookerFactoryNewInstance");
}
}
//CookerFactoryMulty.java
publicclassCookerFactoryMulty{
publicCookerFactoryMulty(){
Log.d("TAG","CookerFactoryMultyNewInstance");
}
}
//除了在Module的Provides方法里写上@Scope还需要在Component类名上方写上,这里自定义的@Scope名字叫PerActivity
@PerActivity
@Component(modules={ActivityModule.class})
publicinterfaceActivityComponent{
voidinject(MainActivitysimpleActivity);
}
publicclassMainActivityextendsActivity{
ActivityComponentactivityComponent;
@Inject
CoffeeShopcoffeeShop1;
@Inject
CoffeeShopcoffeeShop2;
@Inject
CookerFactorycookerFactory1;
@Inject
CookerFactorycookerFactory2;
@Inject
CookerFactoryMultycookerFactoryMulty1;
@Inject
CookerFactoryMultycookerFactoryMulty2;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activityComponent=DaggerActivityComponent.builder()
//下面这句话可以不写,因为Module有默认构造函数。如果Module的构造器里有参数,并且该参数不是注入进去的,就需要用类似下面的方法手动设置实例到Component中
.activityModule(provideModule())
.applicationComponent(MyApplication.getComponent()).build();
activityComponent.inject(this);
coffeeFactory.run();
}
privateActivityModuleprovideModule(){
returnnewActivityModule();
}
}
运行结果
07-1116:53:27.9781927-1927/?D/TAG﹕CoffeeShopNewInstance 07-1116:53:27.9781927-1927/?D/TAG﹕CookerFactoryNewInstance 07-1116:53:27.9781927-1927/?D/TAG﹕CookerFactoryMultyNewInstance 07-1116:53:27.9781927-1927/?D/TAG﹕CookerFactoryMultyNewInstance
@Singleton该注解继承@Scope,用的时候区别就是不用去自定义@Scope了,比如上面定义@PerActivity的这步就不需要了,其他的用法和使用@PerActivity一模一样,也是在Component类名上面和Module的Provides方法里都写上注解。
注意注意注意:再次提醒,局部单例是在同一个Component实例提供依赖的前提下才有效的,不同的Component实例只能通过Component依赖才能实现单例。也就是说,你虽然在两个Component接口上都添加了PerActivity注解或者Singleton注解,但是这两个Component提供依赖时是没有联系的,他们只能在各自的范围内实现单例
在@Inject标注的构造器上使用局部单例直接在类名上声明作用范围(类名上添加@Singleton或自定义Scope)
依赖:dependencies
Component依赖Component的情况下,两个Component的@Scope不能相同,否则会编译错误,为什么这么设计我还不是很清楚,有知道的小伙伴请告诉我谢谢。
依赖的示例代码如下:
//Person.java
publicclassPerson{
}
//PerActivity.java
@Scope
@Retention(RetentionPolicy.RUNTIME)
public@interfacePerActivity{}
//BaseModule.java
@Module
publicclassBaseModule{
@Singleton
@Provides
publicPersonprovidePerson(){
returnnewPerson();
}
}
//Module1.java
@Module
publicclassModule1{
}
//Module2.java
@Module
publicclassModule2{
}
//BaseComponent.java
@Singleton
@Component(modules=BaseModule.class)
publicinterfaceBaseComponent{
publicPersonprovidePerson();
}
//Component1.java
@PerActivity
//@Singleton
//因为依赖(dependencies)的BaseComponent中用到了@Singleton,所以这个Component就不能再用了,否则会编译错误,为什么这么设计还不是很清楚
@Component(modules=Module1.class,dependencies=BaseComponent.class)
publicinterfaceComponent1{
voidinject(TestScopeActivity1simpleActivity);
}
//Component2.java
@PerActivity
//@Singleton为什么不能用?原理同上
@Component(modules=Module2.class,dependencies=BaseComponent.class)
publicinterfaceComponent2{
voidinject(TestScopeActivity2simpleActivity);
}
//MyApplication.java
publicclassMyApplicationextendsApplication{
privatestaticBaseComponentbaseComponent;
@Override
publicvoidonCreate(){
super.onCreate();
baseComponent=DaggerBaseComponent.builder().build();
}
publicstaticBaseComponentgetBaseComponent(){
returnbaseComponent;
}
}
//TestScopeActivity1.java
publicclassTestScopeActivity1extendsActivity{
@Inject
Personp=null;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerComponent1.builder().baseComponent(MyApplication.getBaseComponent()).build().inject(this);
TextViewtextView=findViewById(R.id.textView);
textView.setText(p.toString());
textView.setOnClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
startActivity(newIntent(TestScopeActivity1.this,TestScopeActivity2.class));
}
});
}
}
//TestScopeActivity2.java
publicclassTestScopeActivity2extendsActivity{
@Inject
Personp=null;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerComponent2.builder().baseComponent(MyApplication.getBaseComponent()).build().inject(this);
TextViewtextView=findViewById(R.id.textView);
textView.setText(p.toString());
}
}
daggar2如何选择依赖呢,按照这样的顺序
当Component调用inject方法的时候,会搜索被注入类中用@Inject注解的字段,然后会在该Component中查找在@Component(modules=。。。)注解中注册的Module,如果搜索到Module有@Provides注解的方法提供该@Inject注解的字段所需的实例,就调用相应的方法完成注入,否则就查找所有用@Inject注解构造函数的类,如果找到就调用相应的构造函数完成注入,如果在获得实例的时候还需要获取参数的实例,再按照刚才的流程依次注入参数实例
画个简单的流程,如下所示
Component.inject->在Component搜索Module->找到就调用@Provides注解的方法提供实例
->没找到就搜索@Inject注解的构造函数
->都找不到就报错。。。
都没找到肯定就报错了。。。但是会优先寻找Component注册的Module,而@Inject注册的构造器可以调用任何Component的inject方法完成注入,因为@Inject注册的构造器不需要在Component里注册,这里和Module有区别,Module是需要在某个Component中注册的,而@Inject不需要
举一个MVP中使用Dagger2的示例,我就不贴代码了,直接看下面这个链接好了:https://www.nhooo.com/article/138093.htm
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。