什么是Dagger2
1.Dagger2是一个依赖注入框架。如果Class A 内部 有一个Class B,我们称作Class A依赖Class B
例如:
public class A{
B b
public A(){
b=new B();
}
}
上述方法代码耦合度很高,如果B是一个父类,我们需要在A中用到不同的B类,是需要不断创建对象B b1= new B1(),B b2=new B2();
2.依赖注入:是外界根据需要注入不同的对象。这样可以有效的降低耦合度。
像这种非自己主动初始化依赖,而通过外部来传入依赖的方式,我们就称为依赖注入。
参考资料:https://github.com/android-cn/blog/tree/master/java/dependency-injection
public class A{
B b;
public A(B b){
this.b=b;
}
}
安卓应用在初始化对象的时候经常需要处理各种依赖关系。比如说网络访问中使用Retrofit,Gson,本地存储中使用shared preference。无一例外,我们都都需要在使用它们的地方进行实例对象构建,对象之间可能还存在着各种各样的依赖关系。依赖注入(Dependency Injection,简称DI)是用于削减计算机程序的耦合问题的一个法则。对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。Dagger2 正是一个依赖注入框架,使用代码自动生成创建依赖关系需要的代码。减少很多模板化的代码,更易于测试,降低耦合,创建可复用可互换的模块。
*文/CameloeAnthony(简书作者)
原文链接:http://www.jianshu.com/p/01d3c014b0b1
一、配置Dagger2。
1.在项目的gradle中加入apt的依赖
Paste_Image.png
2.在module的gradle中加入dagger2需要依赖的库
Paste_Image.png
3.在module的gradle中加入插件支持
Paste_Image.png
4.如果已经都配置好了,那么需要makeproject一下。
在AndroidStudio中,点击一下红线标记的按扭
Paste_Image.png
二、Dagger2的使用
图片来自参考资料.png
1、既然是依赖注入,那么肯定有提供依赖的地方,在Dagger2中提供依赖的类被标注为Module,在Module中,具体提供对象的方法,被Provides注解标记。
2、从流程图中可以看出,Module中提供A对象给B和C。传递给B和C的工作由Component注解标记的接口来进行。
3、在B和C哪个对象要通过注入拿到对象则通过Inject注解进行标示。
图片来自参考资料
1.Inject注解
在Activity中,为LogUtils加上Inject注解,告诉编译器LogUtils需要通过注入的方式实例化。同样可以使用@inject注解标注构造方法,方法等。
注意,被inject标注的不能是private修饰的对象。
interface是不能被构建的
第三方的类不能被注解
通过配置得到的对象,仍然必须通过配置得到(Builder模式构造的对象)
如果使用@Inject标注了参数,如果没有通过Module提供依赖,会去查找是否有@Inject标注的构造方法,
使用该构造方法提供依赖,如果该构造方法中也有参数,会去Module中查找是否提供了依赖。
Paste_Image.png
2.Module
Module是一个提供依赖对象的地方,需要使用@Module标注类,使用@Provide标注提供依赖的方法
编译器需要知道LogUtils的对象在哪里能够得到。这时需要一个提供对象的类,加上Module注解
如果使用了@Scope注解为方法标注了使用的作用域,那么Component类上也需要有相应的作用域,否则编译报错。
注意AppModuls的方法名是任意的,为了能更好的读懂代码一般都称作provider+对象名称。
Paste_Image.png
3.Component
1.有了提供的地方,需要一个桥梁让被标记为Inject的对象能够拿到自己对应的对象,因为Moduls类只是提供不负责传递。所以需要一个Component接口,被标记为Component的类就是这个媒介。
我们需要提供一个inject方法,并且参数一定要是对应的Activity,Fragment类,例如我在MainActivity中需要依赖注入,参数一定要为MainActivity,当然Component可以有多个inject方法,为不同的页面提供注入。
全局的AppComponent只能含有一个带参的无返回值方法,用来给对应的地方实行注入。如:
如果当前AppComponent被其他Component依赖了。其他Component提供依赖时,需要显示的提供依赖,即提返回对应依赖的无参方法。如:AppComponent
@Component(modules = {AppModule.class})
public interface AppComponent {
void inject(ContainerActivity activity);
Context getContext();
}
注意除了使用dependencies对当前的component进行扩展,还可以使用subcomponent进行扩展
@dependencies 代表需要哪一个component提供依赖
@module 代表通过哪一个module提供依赖。
如果你的module指定了作用域,那么component也要使用相同的注解标定作用域
component支持多个module 可以使用,号拼接,也可以使用下列写法。
@Module(includes={ModuleA.class,ModuleB.class,ModuleC.class})
public class FruitModule{
...
}
@Component(modules={FruitModule.class}) //添加多个Module
public interface FruitComponent{
...
}
Paste_Image.png
4.在Application中将Component桥梁实例化
如果你的配置都正确,并且makeproject了,编译器会自动为你的Component生成代码,名称是Dagger+你的Component名称。如果没有这个类,请查看在Gradle中配置是否正确,并且是否makeproject了。
Paste_Image.png
5.Activity拿到中介(桥梁Component),为对象注入
提供的中介当然就是我们在Application中实例化的Component了。
Paste_Image.png
三、Component的之间的依赖---SubComponent
除了使用@Component的dependience注解扩展当前component的支持的依赖,还可以通过SubComponent进行扩展,这有点像继承关系。并且父Component不用显示的抛出支持的依赖,只需添加一个返回子Component的方法
下面看一个来自于鸿洋Blog的图片
http://chuansong.me/n/400687651115
使用@SubComponent标记子component
使用SubComponent进行扩展,父component显示的抛出依赖,需要持有子component对象。
通过父Component拿到子Component
注意两个地方
1.AppComponent 是根Component。
2.UserComponent 是子Component,UserComponent是通过appComponent得到。
这就是通过Component的相互依赖做到的。
1、UserComponent
使用@Subcomponent标注
@UserScope
@Subcomponent(modules = UserModule.class)
public interface UserComponent {
AComponent plus(AModule aModule);
}
2、父Component
提供子Component。(返回值为子Component)
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
UserComponent plus(UserModule userModule);
}
三、@Name注解:区分不同对象的实例
该注解用来标识具体需要哪一个依赖。因为往往我们想要的对象不只有一种依赖方式。例如我们需要一个网络请求库,可能有提供缓存的,可能不需要缓存,那么这个时候我们在Moudle中就会提供两种依赖。具体选择哪个则需要通过name标识去取。
例如:
(http://www.jianshu.com/writer#/notebooks/3267733/notes/4938210/preview)
1.Module提供两种实例。
@Provides @Named("cached")@Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
OkHttpClient client = new OkHttpClient();
client.setCache(cache);
return client;
}
@Provides
@Named("non_cached")
@Singleton
OkHttpClient provideOkHttpClient() {
OkHttpClient client = new OkHttpClient();
return client;
}
2.根据需求选取不同的实例
@Inject @Named("cached") OkHttpClient cache_client;
@Inject @Named("non_cached") OkHttpClient non_cache_client;
@Qualifier注解
当然Dagger2通过使用@Qualifier可以定制自己的Name形式注解。
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface UserNamed {
String value() default "";
}
这和Dagger2提供的name的代码实现是一样的
下面是Dagger2提供的name
http://chuansong.me/n/400687651115
Scope注解:用于限定范围,指明Module可以像哪些对象提供依赖
1.定义自己Scope注解
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface UserScope {
}
2.在Module中加入Scope注解,表示为哪些对象提供实例
鸿洋
3.在Component中加入Scope注解,表示在这个Component中,有权利从Module中拿到被Scope标注的对象
@UserScope
@Subcomponent(modules = UserModule.class)
public interface UserComponent {
AComponent plus(AModule aModule);
BComponent plus(BModule bModule);
CComponent plus(CModule cModule);
}
参考资料
http://www.jianshu.com/p/01d3c014b0b1
http://chuansong.me/n/400687651115
http://blog.csdn.net/study_zhxu/article/details/52169090
http://android.jobbole.com/82705/
http://gold.xitu.io/entry/572232fc1532bc00624b5c8e
http://codethink.me/2015/08/06/dependency-injection-with-dagger-2/非常简洁清晰