依赖注入

在介绍 Dagger2 这个之前,必须先解释一下什么是依赖注入,因为这个库就是用来做依赖注入的。所以这里先简单用一句话来介绍一下依赖注入:
依赖注入是一种设计模式,它允许对象在运行时注入其依赖项。而不是在编译时确定(也就是硬编码)。通过这种方式,可以更好地解耦代码,提高测试性和可维护性。
详细了解依赖注入,这里建立先看完这个博文,详细介绍了这个概念和实现方式:Java 基础知识之 依赖注入(Dependency Injection)

Dagger2

上面的博客已经介绍了,依赖注入有很多不同的框架都可以做这个事,那为什么选择 Dagger2 呢?对于后端开发可能会用 Spring,而对于 Android 开发,只能用 Dagger2 了。这主要是因为这个库本身就是由 Google 推出的,而且它通过注解处理器生成高效的依赖注入代码,避免了运行时反射产生的性能开销。在 Android 源代码项目中,广泛使用了这个库。

这里重点注意 Dagger2 这个库与其他依赖注入库的区别在于 Dagger2 使用的是注解处理器,而不是运行时反射。如果不了解这两个方式的区别可以看一些这个:
制作自己的ButterKnife(使用AutoService和APT注解处理器在编译期生成Java代码)
制作自己的 @OnClick、@OnLongClick(告别setOnClickListener,使用注解、反射和动态代理)
使用注解处理器可以在编译时生成代码来完成功能,这比使用运行时反射要快很多。而性能在 Android 这种嵌入式设备中相当重要,因此对于 Android 开发者来说,如果使用依赖注入,这个库就是必选的。

基本概念

在使用 Dagger2 这个库时,主要会有三个角色:

  • 依赖需求方:就是需要依赖对象的那些类。例如一个人想要玩电脑,那么他就必须得有一台电脑,因此这个人就是依赖需求方;
  • 依赖供应方:负责提供依赖对象,类似与实际编码中的工厂类,这个人依赖一台电脑玩游戏,那么就必须有个地方能够提供一台电脑,这个地方就是依赖供应方,顾名思义,就是用于创建以来对象的;
  • 依赖注入器:负责将以来对象注入到以来需求方,在实际代码中是一个接口,编译时 Dagger2 自动生成的就是这个接口的实现类。接着上面的说,这个人是依赖需求方,他需要一台电脑,依赖供应方能够提供一台电脑,可是这两者没有打通啊,电脑没有给到这个人,他还是玩不了游戏啊,因此这个时候就由依赖注入器将这台电脑注入给这个人,他就能够使用这台电脑玩游戏了。

上面已经说得很形象了,大家应该都能理解,不能理解的,可以想象下面的一个场景。
你需要一台电脑打游戏,那么你依赖于电脑,你就是依赖需求方,依赖对象是一台电脑。这台电脑哪里能提供呢?当然是淘宝、京东、实体店了,这些都能提供一台电脑,那么它们都是依赖供应方。但是这中间必须得有个东西把电脑从供应商的仓库送到你手里,你才能用,这就可以理解为将电脑这个依赖对象注入到你手中。什么是依赖注入器呢?在这里例子中,那就是三通一达这些快递公司了。

就是一个简单的购物的流程,只是把依赖注入的概念套进去了而已。下面我们就以这个场景为例,写个 Demo,告诉大家如何使用 Dagger2 这个库。

引入 Dagger2

截止到目前,Dagger2 这个库的最新版本是 2.51.1。引入这个库的方式也很简单,在 build.gradle 中添加如下依赖:

dependencies {
  implementation 'com.google.dagger:dagger:2.51.1'
  annotationProcessor 'com.google.dagger:dagger-compiler:2.51.1'
}

大家再引入的时候最好查看一下 Dagger 的官网,引入最新的版本:https://dagger.dev/
在引入依赖并 Sync Project 之后,你会发现项目的依赖会多出来两个库:
简单介绍 Dagger2 的入门使用-LMLPHP

编写依赖需求方

先编写一个 Person 类,里面有一个 playGame 的方法,这个方法中要使用 Computer,也就是说,Computer 是 Person 的依赖,我们使其成为一个成员变量:

public class Person {
    
    private String name;
    private Computer computer;

    public void Person(String name) {
        this.name = name;    
    }

    public void playGame(String gameName) {
        computer.play(gameName);
    }
}

以下是 Computer 类,作为依赖对象:

public class Computer {

    private String name;

    public Computer(String name) {
        this.name = name;
    }

    public void play(String game) {
        System.out.println("使用 " + name + " 玩 " + game);
    }
}

编写依赖供应方

现在,有了依赖需求方,那就要找到依赖提供商提供一台电脑。哪里能提供电脑呢,那就先编写一个淘宝类吧:

@Module
public class TaoBao {

    private Computer assembleComputer() {         //组装一台电脑
        Computer computer = new Computer("淘宝的电脑");
        return computer;
    }

    @Provides
    public Computer getComputer() {
        return assembleComputer();
    }
}

这里注意两个注解 @Module 和 @Provides,这两个注解是 Dagger 提供的。其中 @Module 用于告知 Dagger 这个类是一个依赖提供商,@Provides 用于告知 Dagger 这个依赖提供商里面哪些方法是用于提供依赖对象的。
在这个例子中, TaoBao 是一个依赖供应方,其中 getComputer 用于提供依赖对象,assembleComputer 则是一个普通方法。

编写依赖注入器

有了需求方和供应方,那么就需要将两者链接起来,依赖对象只有从供应方交给需求方,才有意义,这就是依赖注入器的工作。在这个例子中,依赖注入器就是快递了,快递把电脑从淘宝店家送到买家手中。这里我们就先编写一个中通吧:

@Component(modules = TaoBao.class)
public interface ZTOExpress {
    void deliverTo(Person person);
}

注意这个注入器是一个 interface 而非 class,在编译时,Dagger 会生成对应的实现类。
这个接口添加了一个注解:@Component,这个注解是就是告诉注入器,从哪个依赖供应方拿依赖对象。这段代码里,@Component 注解告知了中通,去淘宝拿电脑快递给买家。
但还有一个问题,中通知道将电脑配送给买家,那配送到那个成员变量呢?Person 里有 name 和 computer,从名字上就能看到电脑肯定要配送到 computer 的成员变量上,这个时候需要将 computer 这个成员变量添加 @Inject 注解:

public class Person {
    
    private String name;
    @Inject
    private Computer computer;
    
    //......
}

依赖注入结果

现在三个角色都有了,那我们现在就把它们拼接在一起,看看效果吧。

Person person = new Person("张三");
ZTOExpress ztoExpress = DaggerZTOExpress.builder().taoBao(new TaoBao()).build();
ztoExpress.deliverTo(person);
person.playGame("赛博朋克2077");

输入:

System.out    I  使用 淘宝的电脑 玩 赛博朋克2077

总结

先讲到这里。后续再补全。
简单介绍 Dagger2 的入门使用-LMLPHP

07-09 13:41