1.Dagger2简介
1.1 Dagger2的描述
Github地址:Dagger2
Dagger2官网上介绍是:A fast dependency injector for Android and Java.
Dagger2是提供给Android和Java使用的快速依赖注入框架,目的是为了解耦.
1.2 Dagger2的前世今生
Dagger2是由谷歌接手开发,最早版本的Dagger1 是由Square公司开发的.
相比于Dagger1,Dagger2有更好的性能,它使用的是预编译期间生成代码来完成依赖注入,而不像Dagger1那样通过反射完成依赖注入;还有一点,因为Dagger2是使用生成代码来实现依赖注入的,所以可以在相关代码处下断点进行调试.
1.3 Dagger2的用处
使用Dagger2最本质的目的是为了模块间解耦,提供代码的健壮性和可维护性.
举个简单的例子
public class A{
B b;
public A(){
b = new B()
}
}
如上代码,在ClassA对ClassB有个依赖,如果某一天ClassB的创建方式发生改变,即构造方法发生改变,这时不仅要修改ClassB中的代码,还需要在所有创建ClassB的类中进行修改,如上面的ClassA.
如果使用Dagger2的话,在ClassA中可以这样写:
public class A{
@Inject
B b;
}
此时不管B的创建方式如何修改,在ClassA中都不需要进行修改.
2.Dagger2的原理
如上图所示:
最左边,是提供依赖的部分,可以通过使用@Inject注解其构造方法或者在Module中使用@Provides注解提供.
中间部分是Component,它本质上是被@Component注解的接口,通过@Component注解与Module建立联系,并提供inject()函数.它是一个桥梁,负责连接提供依赖的部分和需要依赖的部分,
最右边是需要依赖的地方,使用@Inject注解变量,就可以创建它的对象.
3.Dagger2的使用
3.1 Android studio中配置
在工程的build.gradle文件中添加android-apt插件
dependencies {
... // 其他classpath
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' //添加apt命令
}
module的build.gradle添加
apply plugin: 'com.neenbedankt.android-apt'//添加apt命令
dependencies {
apt 'com.google.dagger:dagger-compiler:2.16' //指定注解处理器
compile 'com.google.dagger:dagger:2.16' //dagger公用api
provided 'org.glassfish:javax.annotation:10.0-b28' //添加android缺失的部分javax注解库
}
但是当Android studio升级到3.0之后,就会出现以下错误:
Error:android-apt plugin is incompatible with the Android Gradle plugin. Please use 'annotationProcessor' configuration instead.
解决办法:Studio升级到3.0之后原来的配置方式apt与最新版本Gradle已经不兼容,推荐使用annotationProcessor
只需要添加这三个依赖就可以,工程的build.gradle与app的build.gradle插件可不用添加
首先在Android studioModule的Gradle中添加如下代码:
// Add Dagger dependencies
dependencies {
compile 'com.google.dagger:dagger:2.16'
annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
compile 'org.glassfish:javax.annotation:10.0-b28' //添加android缺失的部分javax注解库
}
3.2 常用注解
@Inject:
通常在需要依赖的地方使用这个注解。换句话说,你用它告诉Dagger这个类或者字段需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。
@Module:
Modules类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的 依赖。modules的一个重要特征是它们设计为分区并组合在一起(比如说,我们的app中可以有多个组成在一起的modules)
@Provide:
在modules中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。
@Component:
Components从根本上来说就是一个注入器,也可以说是@Inject和@Module的桥梁,它的主要作用就是连接这两个部分。
Components可以提供所有定义了的类型的实例,比如:我们必须用@Component注解一个接口然后列出所有所提供的依赖.
@Qualifier:
要作用是用来区分不同对象实例
@Named 其实是@Qualifier的一种实现
3.3 简单示例
被需要依赖的类
public class AManager {
public void makeShow() {
Toast.makeText(AppUtils.getApp(), "Hello Dagger2!!!", Toast.LENGTH_SHORT).show();
}
}
提供依赖的类:Module
@Module
public class ManagerModule {
@Provides
public AManager provideManagerA(String name) {
Log.e("TAG", "provideManagerA");
return new AManager();
}
}
中间桥梁:Contenent
@Component(modules = {ManagerModule.class})
public interface ManagerComponent {
void inject(MainActivity activity);
}
需要依赖的类:Container
public class MainActivity extends AppCompatActivity {
@Inject
AManager aManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerManagerComponent.create().inject(this);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
aManager.makeShow();
}
});
}
}
3.4 关于模块化的实现
在项目中,出于更加灵活以及可维护等其他原因,常进行模块化的划分,比如B功能的实现需要A,C功能的实现也需要A,那么我们就需要单独对A进行处理,以便于其他功能模块的使用.
Dagger有三种途径进行模块之间的依赖:
方式一:Module中通过@Module注解,includes其他的Module
@Module(includes = {xxxModule.class})
方式二:Component中通过@Component注解关联需要的其他Module
@Component(modules = {ManagerModule.class,xxxxModule.class})
方式三:Component中通过@Component注解dependencies 其他Component
@Component(modules = {ManagerModule.class},dependencies = {xxxComponent.class})
注意事项:
通过方式三与其他模块关联时,被需要依赖的类的构造方法需要使用@Inject进行注解,同时在注入Container的时候把其他的Component进行注入.
3.5 创建和区分不同实例
>>>>>>>方式一:使用@Named注解
@Module
public class ManagerBModule {
@Named("release")
@Provides
public BManager provideManagerB_Release(AManager aManager) {
BManager bManager = new BManager(aManager);
Log.e("TAG", "provideManagerB_Release:" + bManager);
return bManager;
}
@Named("debug")
@Provides
public BManager provideManagerB_Debug(AManager aManager) {
BManager bManager = new BManager(aManager);
Log.e("TAG", "provideManagerB_Debug:" + bManager);
return bManager;
}
}
@Named("release")
@Inject
BManager bManager_release;
@Named("debug")
@Inject
BManager bManager_debug;
>>>>>>>方式二:使用自定义注解
模仿Named注解,进行自定义
自定义Release注解:
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Release {
}
自定义Debug注解:
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Debug {
}
在Module中使用:
@Module
public class ManagerBModule {
@Release
@Provides
public BManager provideManagerB_Release(AManager aManager) {
BManager bManager = new BManager(aManager);
Log.e("TAG", "provideManagerB_Release:" + bManager);
return bManager;
}
@Debug
@Provides
public BManager provideManagerB_Debug(AManager aManager) {
BManager bManager = new BManager(aManager);
Log.e("TAG", "provideManagerB_Debug:" + bManager);
return bManager;
}
}
在Activity中的使用:
@Release
@Inject
BManager bManager_release;
@Debug
@Inject
BManager bManager_debug;
3.6 Singleton单例讲解
使用@Singleton注解,需要注意的是如果Module中,@Provides注解的方法使用了@Singleton注解,那么与之对应的Component也要加上@Singleton注解.
Module代码:
Component代码:
Activity代码:
点击按钮后的Log日志:
注意事项:
Dagger2中的单例与Java中的单例不同,Java中的单例对象是存放在静态区的,而Dagger2的单例是与Component相关联的.不同Component所提供的实例化的单例对象是不同的.
3.7关于自定义Scope
由于component的dependencies与component自身的scope不能相同,即组件之间的scope不同,因此需要自定义一些Scope
对于应用级的AppComponent常使用@Singleton,以使某些对象在整个程序中只实例化一次
Module:
Component:
Application:
自定义的ActivityScope
依赖AppComponent的Component:
Activity代码:
关于Scope推荐一篇文章:Dagger2Scope讲解
有关Scope注解参照@Singleton注解
@Scope :注明是Scope ,表示作用域范围
@Documented :标记在文档
@Retention(RUNTIME) :运行时级别
Scope的生命周期是依附于其容器的,当其所依附的Application或者是Activity销毁后,其关联的Module中的实例化对象也会被销毁.
3.8关于Subcomponent
在Dagger2中,Component之间存在两种关系,依赖关系和继承关系
依赖关系:一个 Component 依赖其他 Component 公开的依赖实例,使用 @Component 注解中的dependencies进行声明.
继承关系:一个 Component 继承某个Component 提供更多的依赖,SubComponent 就是继承关系的体现.
两者之间的比较:
>相同点:
两者都能复用其他 Component 的依赖
有依赖关系和继承关系的 Component 不能有相同的 Scope
>不同点:
依赖关系中被依赖的 Component 必须显式地提供公开依赖实例的接口,而 SubComponent 默认继承 parent Component 的依赖.
依赖关系会生成两个独立的 DaggerXXComponent 类,而 SubComponent 不会生成 独立的 DaggerXXComponent 类.
示例:
父Component的Module:
父Component:
子Component的Module相关:
子Component:
Activity代码:
注意事项:
1>Subcomponent同时具备两种不同生命周期的scope, SubComponent具备了父Component拥有的Scope,也具备了自己的Scope.
2>SubComponent的Scope范围小于父Component
3.9拓展-关于Lazy与Provider:
Lazy(延时注入):
有时当需要注入的依赖在使用时再完成初始化,加快加载速度,就可以使用注入Lazy
好比component初始化了一个present对象,然后放到一个池子里,需要的时候就get它,所以你每次get的时候拿到的对象都是同一个;并且当你第一次去get时,它才会去初始化这个实例.
provider(强制加载):
有时候不仅仅是注入单个实例,我们需要多个实例,这时可以使用注入Provider<>,每次调用它的 get() 方法都会调用到 @Inject 构造函数创建新实例或者 Module 的 provide 方法返回实例。
注意事项:
要想调用Porvder的get()到的实例对象不同,必须其Module中提供实例的方法没有被@Scope注解,不然所获取到的是同一对象.
4 注意事项
1. Component 的 inject(自定义,可以随便起名) 方法如果接收的是父类型参数,而调用时传入的是子类型对象则无法实现注入;
2. Component中使用@Component注解关联的modules中的Module内不能有重复的provide,除非使用@Qualifier注解进行限定,如@Named注解;
3. Module的@Provides注解的方法使用了 Scope ,那么与其关联的Component 就必须使用同一个Scope注解;如果Module中@Provides注解的方法没有使用Scope,那么与其关联的Component是否加Scope注解都无所谓,可以通过编译;
4.Component中使用@Component注解声明的dependencies与Component自身的Scope不能相同,即组件之间的Scope必须不同;
5.@Singleton注解的组件不能依赖其他Scope的组件,只能其他Scope的组件依赖Singleton的组件;
6.没有Scope的Component不能依赖有Scope的Component;
7.一个Component不能同时有多个scope(Subcomponent除外);
8.@Singleton 的生命周期依附于Component,同一个Module provide singleton实例对象 ,不同Component 也是不一样的;
9.已使用Component注入的Container不能被其他Component二次注入.