目录
- 一、Dagger简介
-
- [1.1 什么是Dagger?](#1.1 什么是Dagger?)
- [1.2 Dagger用来干什么?](#1.2 Dagger用来干什么?)
- [1.3 使用Dagger2注入对象](#1.3 使用Dagger2注入对象)
- [1.4 Dagger注解](#1.4 Dagger注解)
- 二、Dagger2使用
-
- [2.1 非单例](#2.1 非单例)
- [2.2 局部单例](#2.2 局部单例)
- [2.3 全局单例](#2.3 全局单例)
- 三、参考链接
一、Dagger简介
1.1 什么是Dagger?
Dagger 2 是一个由 Google 开发的依赖注入框架
,用于帮助开发者实现依赖注入
(Dependency Injection)的设计模式。依赖注入是一种软件设计模式,用于减少组件之间的耦合,提高代码的可维护性、可测试性和可扩展性。
什么是依赖注入?
维基百科上面的介绍是:在软件工程中,依赖注入是种实现控制反转用于解决依赖性设计模式。一个依赖关系指的是可被利用的一种对象(即服务提供端) 。依赖注入是将所依赖的传递给将使用的从属对象(即客户端)。该服务是将会变成客户端的状态的一部分。 传递服务给客户端,而非允许客户端来建立或寻找服务,是本设计模式的基本要求。
简单来说依赖注入就是将实例对象传入到另一个对象中去。
1.2 Dagger用来干什么?
- 简化代码:通过依赖注入,Dagger 2 可以帮助开发者管理类之间的依赖关系,避免手动创建和管理对象实例,从而简化代码结构。
- 提高可测试性:依赖注入可以帮助开发者轻松地替换依赖的对象,从而更容易进行单元测试和集成测试。
- 解耦合:通过依赖注入,可以将类之间的依赖关系从代码中解耦合,使得代码更加灵活和可维护。
- 提高可维护性:依赖注入可以帮助开发者更好地组织和管理代码,减少重复代码,使得代码更易于理解和维护。
- 提高可扩展性:依赖注入可以帮助开发者更容易地扩展和修改代码,使得系统更具弹性和可扩展性。
假如我们的Activity使用Book 实例,且在Activity中使用了成百上千次此实例,此时修改Book()构造器由默认构造器修改为带参的构造器Book(int value),修改Book相关方法,那么Activity里岂不是要修改成百上千次?严重违背了设计模式中的开闭原则
,两者之间的耦合度太高,而依赖注入框架Dagger就是能将类之间的依赖关系从代码中解耦合,使得代码更加灵活和可维护。
java
public class UserActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user);
Book book=new Book();
}
..........
上述场景在Dagger下使用流程大概如下:
1.3 使用Dagger2注入对象
Dagger流程就像买快递:用户user购买Book,使用Dagger2实现。
添加依赖:
java
implementation 'com.google.dagger:dagger-android:2.17'
// implementation 'com.google.dagger:dagger-android-support:2.17' // if you use the support libraries
implementation 'com.google.dagger:dagger:2.17'
annotationProcessor 'com.google.dagger:dagger-compiler:2.17'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.17'
Book类:
java
public class Book {
@Inject
public Book() {
}
}
包裹:BookModule
java
@Module //APT技术,Arouter、DataBinding、Room,基本上都是APT编译器生成代码
public class BookModule {
@Provides
public Book GetBook() {
return new Book();
}
}
快递员:BookComponent
java
@Component(modules = BookModule.class)
public interface BookComponent {
//注入用户收获地址
void injectUser(UserActivity userActivity);
@Component.Builder
interface Builder {
BookComponent build();
}
}
用户 UserActivity
java
public class UserActivity extends AppCompatActivity {
@Inject
Book book;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user);
DaggerBookComponent.create().injectUser(this);
Log.d(Tag," --"+book.hashCode());
}
}
其中DaggerBookComponent
需要工程make一下编译产出才能使用,Dagger2较其它依赖注入工具有一个优势,就是它是采用静态编译的方式编译代码的,会在编译期生成好辅助代码,不会影响运行时性能。
测试一下:Book没有报错,确实已经实例化了。
1.4 Dagger注解
@Component
:@Component 是 Dagger 中最重要的注解之一,用于标记一个接口或抽象类作为 Dagger Component。Component 负责连接依赖的提供者(Module)和依赖的消费者(被注入对象)。在 @Component 注解中,通常会指定一个或多个 Module 类,以便 Dagger 可以从这些 Module 中获取依赖的实例。@Component.Builder
:@Component.Builder 是用于构建 Dagger Component 实例的接口。通过 Builder 接口,可以定义创建 Component 实例的方法,并在需要时配置 Component。Builder 接口通常用于自定义 Component 的创建过程,例如指定特定的 Module 实例或其他配置。@Module
:@Module 是用于提供依赖实例的类的注解。在 Module 类中,通过 @Provides 注解的方法来提供依赖的实例。Module 类的主要作用是告诉 Dagger 如何创建和提供依赖实例,以便在需要时注入到其他类中。@Provides
:@Provides 是用于标记 Module 类中提供依赖实例的方法的注解。在 @Provides 注解的方法中,通常会返回一个实例化的对象,并通过方法名指定要提供的依赖类型。Dagger 在需要依赖实例时会调用这些被 @Provides 注解的方法来获取实例。@Inject
:@Inject 是用于标记类构造函数、字段或方法的注解,表示这些元素需要依赖注入。当 Dagger 遇到被 @Inject 注解的构造函数时,会尝试实例化该类并满足其依赖。@Inject 还可以用于标记成员变量或方法,指示 Dagger 在创建对象实例时注入这些成员变量或调用这些方法。
通过合理使用这些注解,可以在 Dagger 中实现依赖注入的整个流程,从提供依赖的 Module 到消费依赖的 Component,以及标记需要注入的类和成员。这样可以帮助开发者更好地管理类之间的依赖关系,提高代码的可维护性和可测试性。
通过注解处理器(APT)编译处理注解标识的方法,生成DaggerxxxComponent
等文件,进而内部实现注入。
二、Dagger2使用
使用Dagger2依赖注入Coffee----------探索如何实现Coffee全局单例注入的过程
2.1 非单例
依然按照上面的demo,测试一下注入两个类一样的对象,观察哈希值判断是否单例
Coffee
java
public class Coffee {
public Coffee() {
}
}
CoffeeModule
java
@Module
public class CoffeeModule {
@Provides
public Coffee GetCoffee() {
return new Coffee();
}
}
CoffeeComponent
java
@Component(modules = {CoffeeModule.class})
public interface CoffeeComponent {
void injectActivity_first(Activityfirst activityfirst );
void injectActivity_second(Activitysecond activitysecond );
}
Activityfirst
java
public class Activityfirst extends AppCompatActivity implements View.OnClickListener {
Button button;
@Inject
Coffee coffee1;
@Inject
Coffee coffee2;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_activityfirst);
button = findViewById(R.id.Button_1);
button.setOnClickListener(this::onClick);
//方式一
// DaggerCoffeeComponent.builder()
// .coffeeModule(new CoffeeModule()).build()
// .injectActivity_first(this);
//
//方式二
DaggerCoffeeComponent.create().injectActivity_first(this);
Log.d("Henry", " ------" + coffee1.hashCode());
Log.d("Henry", " ------" + coffee2.hashCode());
}
@Override
public void onClick(View v) {
startActivity(new Intent(this, Activitysecond.class));
}
}
测试结果:
很明显,两个对象哈希值不同,默认不是单例实现。
2.2 局部单例
使用@Singleton注解实现局部单例
Coffee不变
java
public class Coffee {
public Coffee() {
}
}
CoffeeModule 方法添加@Singleton
java
@Module
public class CoffeeModule {
@Singleton
@Provides
public Coffee GetCoffee() {
return new Coffee();
}
}
CoffeeComponent 接口添加@Singleton
java
@Singleton
@Component(modules = {CoffeeModule.class})
public interface CoffeeComponent {
void injectActivity_first(Activityfirst activityfirst );
void injectActivity_second(Activitysecond activitysecond );
}
Activityfirst不变
java
public class Activityfirst extends AppCompatActivity implements View.OnClickListener {
Button button;
@Inject
Coffee coffee1;
@Inject
Coffee coffee2;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_activityfirst);
button = findViewById(R.id.Button_1);
button.setOnClickListener(this::onClick);
//方式一
// DaggerCoffeeComponent.builder()
// .coffeeModule(new CoffeeModule()).build()
// .injectActivity_first(this);
//
//方式二
DaggerCoffeeComponent.create().injectActivity_first(this);
Log.d("Henry", " ------" + coffee1.hashCode());
Log.d("Henry", " ------" + coffee2.hashCode());
}
@Override
public void onClick(View v) {
startActivity(new Intent(this, Activitysecond.class));
}
}
Activitysecond
java
public class Activitysecond extends AppCompatActivity {
@Inject
Coffee coffee3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_activitysecond);
//方式一
// DaggerCoffeeComponent.builder()
// .coffeeModule(new CoffeeModule()).build()
// .injectActivity_second(this);
//
//方式二
DaggerCoffeeComponent.create().injectActivity_second(this);
Log.d("Henry", " ------" + coffee3.hashCode());
}
}
测试结果:同一个Activity下哈希值一致,但是再另一个Activity又重新实例化对象,局部单例。
2.3 全局单例
如何实现全局单例?局部单例搭配application实现
局部单例代码不变,添加application和修改CoffeeComponent初始化形式
SingletonApplication
java
public class SingletonApplication extends Application {
private CoffeeComponent myCoffeeComponent;
@Override
public void onCreate() {
super.onCreate();
myCoffeeComponent = DaggerCoffeeComponent.builder()
.coffeeModule(new CoffeeModule())
.build();
}
public CoffeeComponent getMyCoffeeComponent() {
return myCoffeeComponent;
}
}
Activityfirst
java
public class Activityfirst extends AppCompatActivity implements View.OnClickListener {
Button button;
@Inject
Coffee coffee1;
@Inject
Coffee coffee2;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_activityfirst);
button = findViewById(R.id.Button_1);
button.setOnClickListener(this::onClick);
//方式三
((SingletonApplication)getApplication())
.getMyCoffeeComponent()
.injectActivity_first(this);
Log.d("Henry", " ------" + coffee1.hashCode());
Log.d("Henry", " ------" + coffee2.hashCode());
}
@Override
public void onClick(View v) {
startActivity(new Intent(this, Activitysecond.class));
}
}
Activitysecond
java
public class Activitysecond extends AppCompatActivity {
@Inject
Coffee coffee3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_activitysecond);
//方式三
((SingletonApplication) getApplication())
.getMyCoffeeComponent()
.injectActivity_second(this);
Log.d("Henry", " ------" + coffee3.hashCode());
}
}
测试结果:哈希值一致了。
如果工程很大,需要代码解耦和单例,完全可以使用这种方法满足对应场景。