Dagger2相关知识

目录

  • 一、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());
    }
}

测试结果:哈希值一致了。

如果工程很大,需要代码解耦和单例,完全可以使用这种方法满足对应场景。


三、参考链接

Dagger2 For Android最佳实践教程
Dagger 2 使用及原理
Dagger2快速入门和原理解析

相关推荐
loveLifeLoveCoding1 分钟前
Java List sort() 排序
java·开发语言
草履虫·8 分钟前
【Java集合】LinkedList
java
AngeliaXue10 分钟前
Java集合(List篇)
java·开发语言·list·集合
世俗ˊ11 分钟前
Java中ArrayList和LinkedList的比较
java·开发语言
Mercury Random11 分钟前
Qwen 个人笔记
android·笔记
zhouyiddd15 分钟前
Maven Helper 插件
java·maven·intellij idea
苏苏码不动了18 分钟前
Android 如何使用jdk命令给应用/APK重新签名。
android
攸攸太上24 分钟前
Docker学习
java·网络·学习·docker·容器
Milo_K31 分钟前
项目文件配置
java·开发语言
aqi0034 分钟前
FFmpeg开发笔记(五十三)移动端的国产直播录制工具EasyPusher
android·ffmpeg·音视频·直播·流媒体