在Android中Dagger2主要目的是解耦,一般结合MVP达到完全解耦的效果。
(1)什么是Dagger2?
Dagger是为Android和Java平台提供的一个完全静态的,在编译时进行依赖注入的框架,原来是由Square公司维护,现在由Google维护。
(2)存在的意义
我们在activity中有可能会用到很多很多的类,这些类要在activity中进行实例化,这样就导致我们的activity非常依赖这么多的类,这样的程序耦合非常严重,不便于维护和扩展,有什么办法可以不去依赖这些类呢,这时候就需要有一个容器,将这些类放到这个容器里并实例化,我们activity在用到的时候去容器里面取就可以了,我们从依赖类到依赖这个容器,实现了解耦,这就是我所理解的依赖注入,即所谓控制反转。
(3)注解之@Inject
主要有两个作用,一个是使用在构造函数上,通过标记构造函数让Dagger2来使用(Dagger2通过Inject标记可以在需要这个类实 例的时候来找到这个构造函数并把相关实例new出来)从而提供依赖,另一个作用就是标记在需要依赖的变量让Dagger2为其提供依赖。
(4)注解之@Module
用Module标注的类是专门用来提供依赖的。有的人可能有些疑惑,看了上面的@Inject,需要在构造函数上标记才能提供依赖,那么如果我们需要提供的类构造函数无法修改怎么办,比如一些jar包里的类,我们无法修改源码。这时候就需要使用Module了。Module可以给不能修改源码的类提供依赖,当然,能用Inject标注的通过Module也可以提供依赖
(5)注解之@Provides
用Provide来标注一个方法,该方法可以在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了@Inject的变量赋值。provide主要用于标注Module里的方法
(6)注解之@Component
Component一般用来标注接口,被标注了Component的接口在编译时会产生相应的类的实例来作为提供依赖方和需要依赖方之间的桥梁,把相关依赖注入到其中。
(7)添加依赖
implementation 'com.google.dagger:dagger:2.22.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
(8)@Inject和@Component使用
我们先看一下未注入Dagger的代码
public class MainActivity extends BaseActivity {
private DaggerTest daggerTest;
@SuppressLint("CheckResult")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
daggerTest = new DaggerTest();
findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
daggerTest.print();
}
});
}
}
public class DaggerTest {
/**
* 打印测试数据
*/
public void print(){
Log.d("aaa", "做一个小测试");
}
}
在MainActivity中创建一个DaggerTest对象,这样的话MainActivity和DaggerTest就产生了依赖关系,这就产生了耦合。下面将使用Dagger来解耦:
第一步
在MainActivity中使用@Inject注解
public class MainActivity extends BaseActivity {
@Inject
DaggerTest daggerTest;
@SuppressLint("CheckResult")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
daggerTest.print();
}
});
}
}
第二步
在DaggerTest的构造方法中使用@Inject注解(构造方法不可以是private)
public class DaggerTest {
@Inject
public DaggerTest(){
}
/**
* 打印测试数据
*/
public void print(){
Log.d("aaa", "做一个小测试");
}
}
第三步
新建一个TestComponent接口,并用@Component修饰
Component是连接注入类和目标类的桥梁。
@Component
public interface TestComponent {
void inject(MainActivity mainActivity);
}
Component会查找目标类中用Inject注解标注的属性,查找到相应的属性后会接着查找该属性对应的用Inject标注的构造函数
第四步
将目标类注入到Component中
我们在MainActivity中的onCreate方法中添加以下代码
DaggerTestComponent.create().inject(this);
这种方式的局限性:
- DaggerTest构造方法不可以带有参数;
- DaggerTest不可以存在多个构造方法;
(9)解决第(8)
项构造方法中不能带参数的问题
要想构造方法中可以带有参数,需要@Module和@Provides的支持。
第一步
在MainActivity中使用@Inject注解
public class MainActivity extends BaseActivity {
@Inject
DaggerTest daggerTest;
@SuppressLint("CheckResult")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
daggerTest.print();
}
});
}
}
第二步
在DaggerTest的构造方法中使用@Inject注解
public class DaggerTest {
@Inject
public DaggerTest(String s){
}
/**
* 打印测试数据
*/
public void print(){
Log.d("aaa", "做一个小测试");
}
}
第三步
新建Model
@Module
public class TestModel {
@Provides
public DaggerTest provideDaggerTest(){
return new DaggerTest("a");
}
}
Model类使用@Module
修饰,@Provides
修饰任意方法并提供对象的实例。
第四步
新建一个TestComponent接口,用@Component修饰,并指定某Model
@Component(modules = TestModel.class)
public interface TestComponent {
void inject(MainActivity mainActivity);
}
第五步
将目标类注入到Component中
我们在MainActivity中的onCreate方法中添加以下代码
DaggerTestComponent.builder()
.testModel(new TestModel())
.build()
.inject(this);
当然TestModel的构造方法可以带有参数
DaggerTestComponent.builder()
.testModel(new TestModel("我传递了一个数据"))
.build()
.inject(this);
这种方式的局限性:
- DaggerTest不可以存在多个构造方法;
(10)注入类拥有多个构造方法的情况
使用@Named
可以使构造方法分类,我就不分步骤了,直接贴一下代码:
public class MainActivity extends BaseActivity {
@Named("type1")
@Inject
DaggerTest daggerTest1;
@Named("type2")
@Inject
DaggerTest daggerTest2;
@Named("type3")
@Inject
DaggerTest daggerTest3;
@SuppressLint("CheckResult")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerTestComponent.builder()
.testModel(new TestModel("我传递了一个数据"))
.build()
.inject(this);
findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
daggerTest1.print();
daggerTest2.print();
daggerTest3.print();
}
});
}
}
public class DaggerTest {
private String printStr = "";
public DaggerTest(){
}
public DaggerTest(String s){
printStr = s;
}
public DaggerTest(String s, String ss){
printStr = s + ss;
}
/**
* 打印测试数据
*/
public void print(){
if(!TextUtils.isEmpty(printStr)){
Log.d("aaa", printStr);
}else{
Log.d("aaa", "做一个小测试");
}
}
}
@Module
public class TestModel {
private String test;
public TestModel(String s){
test = s;
}
@Named("type1")
@Provides
public DaggerTest provideDaggerTest1(){
return new DaggerTest();
}
@Named("type2")
@Provides
public DaggerTest provideDaggerTest2(){
return new DaggerTest(test);
}
@Named("type3")
@Provides
public DaggerTest provideDaggerTest3(){
return new DaggerTest(test, "1");
}
}
@Component(modules = TestModel.class)
public interface TestComponent {
void inject(MainActivity mainActivity);
}
(11)自定义注解替代@Named
@Qualifier
注解可以解决注入类拥有多个构造方法时依赖冲突的问题,下面我们自定义一个注解@CustomName
。
@Qualifier
@Documented
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RUNTIME)
public @interface CustomName {
String value() default "";
}
我们只要将(10)
中的@Named
换成@CustomName
即可。
(12)单例模式
使用@Singleton
注解实现单例。
第一步
在Component中添加@Singleton
@Singleton
@Component(modules = TestModel.class)
public interface TestComponent {
void inject(MainActivity mainActivity);
}
第二步
在Model中的被@Provides
修饰的方法上添加@Singleton
@Singleton
@Provides
public DaggerTest provideDaggerTest1(){
return new DaggerTest();
}
以上两部可以实现局部单例。
当我们从各种Activity中都使用了以下代码
DaggerTestComponent.builder()
.testModel(new TestModel("我传递了一个数据"))
.build()
.inject(this);
那么DaggerTest只在各个Activity中是单例的,不同Activity中的DaggerTest的hashcode不同。现在我们将局部单例
改造成全局单例
:
- 自定义Application
public class MyApp extends Application {
private static TestComponent testComponent;
@Override
public void onCreate() {
super.onCreate();
testComponent = DaggerTestComponent.builder()
.testModel(new TestModel("我传递了一个数据"))
.build();
}
public static TestComponent getTestComponent() {
return testComponent;
}
}
public static TestComponent getTestComponent() {
return testComponent;
}
-
在Activity引用Activity中的Component对象
MyApp.getTest().inject(this);
(13)自定义Scope
自定义Scope比较简单,现在我们想定义一个@MyScope
注解,代码如下
@Scope
@Documented
@Retention(RUNTIME)
public @interface MyScope {
}
而@Singleton
则是@Scope
的默认实现,所以也可以写成
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton{
}
(14)延迟注入(Lazy)
有时我们想注入的依赖在使用时再完成初始化,加快加载速度,就可以使用注入Lazy<T>。只有在调用 Lazy<T>
的 get()
方法时才会初始化依赖实例注入依赖。
@Inject
Lazy<DaggerTest> daggerTest1;
daggerTest1.get().print(String.valueOf(daggerTest1.hashCode()));
(15)多个依赖的情况
假设有A,B两个类,在A中新建一个B对象,那么A和B就产生了依赖,我们称之为:A依赖于B
。
结合上面讲解到的知识,我们很容易就会想到如何使用Dagger来解耦:
第一步
在A中使用@Inject
修饰B的成员变量
@Inject
A a;
第二步
使用@Module
和@Provides
新建一个Model
@Module
public class BModel {
@Provides
public DaggerTest provideDaggerB(){
return new B();
}
}
第三步
使用@Component
定义接口,这个接口作为A和B形成依赖关系的桥梁
@Component(modules = BModel.class)
public interface ABComponent {
void inject(A a);
}
第四步
在A中使用以下代码,即可完成A和B的依赖关系。
ABComponent abComponent = DaggerABComponent.builder()
.testModel(new BModel())
.build()
.inject(this);
以上四个步骤比较简单,但是这时又来了一个C类,在C类中新建一个B对象,那么C和B就产生了依赖,我们称之为C依赖于B
。
我们可以在@Component
新增接口可以实现
@Component(modules = BModel.class)
public interface ABComponent {
void inject(A a);
void inject(C c);
}
(16)一个桥梁可以引用多个modules
@Component(modules = {TestModel.class, Test2Model.class})
public interface TestComponent {
void inject(MainActivity mainActivity);
}
(17)Component的依赖关系
Component就是桥梁,如果某桥梁想要使用另一个桥梁的方法时,可通过依赖实现。
第一步
使用@Inject
声明成员变量
@Inject
DaggerTest2 daggerTest2;
第二步
新建Modules
@Module
public class Test2Model {
@Provides
public DaggerTest2 provideDaggerTest1(){
return new DaggerTest2();
}
}
第三步
桥梁
@Component(modules = {TestModel.class})
public interface TestComponent {
void inject(MainActivity mainActivity);
DaggerTest getDaggerTest();
}
@Component(modules = Test2Model.class, dependencies = TestComponent.class)
public interface Test2Component {
void test(DemoActivity mainActivity);
}
第四步
产生依赖,并调用依赖桥梁的方法
DaggerTest daggerTest = DaggerTestComponent.create().getDaggerTest();
daggerTest.print("aaaa");
DaggerTest2Component.builder()
.testComponent(DaggerTestComponent.create())
.test2Model(new Test2Model())
.build()
.test(this);
(18)可参考的其它博客
Dagger 2 完全解析(一),Dagger 2 的基本使用与原理
Dagger 2 完全解析(二),进阶使用 Lazy、Qualifier、Scope 等
Dagger 2 完全解析(三),Component 的组织关系与 SubComponent
Dagger 2 完全解析(四),Android 中使用 Dagger 2
Dagger 2 完全解析(五),Kotlin 中使用 Dagger 2
Dagger 2 完全解析(六),dagger.android 扩展库的使用