Cursor blinking

Dagger 使用

Java 基础|字数 1,787|阅读时长≈ 5 分钟

Dagger2 常用注解说明

Dagger2 是基于 Java 注解来实现依赖注入的,Dagger2使用过程中我们通常接触到的注解主要包括:@Inject、@Module、@Provides、@Component、@Qulifier、@Scope、@Singleten。

注解释义
@Inject@Inject 有两个作用,一是用来标记需要依赖的变量,以此告诉 Dagger2 为它提供依赖;二是用来标记构造函数,Dagger2 通过 @Inject 注解可以在需要这个类实例的时候来找到这个构造函数并把相关实例构造出来,以此来为被 @Inject 标记了的变量提供依赖;
@Module@Module 用于标注提供依赖的类。你可能会有点困惑,上面不是提到用 @Inject 标记构造函数就可以提供依赖了么,为什么还需要 @Module?很多时候我们需要提供依赖的构造函数是第三方库的,我们没法给它加上 @Inject 注解,又比如说提供以来的构造函数是带参数的,如果我们之所简单的使用 @Inject 标记它,那么他的参数又怎么来呢?@Module 正是帮我们解决这些问题的。
@Provides@Provides用于标注Module所标注的类中的方法,该方法在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了@Inject的变量赋值;
@Component@Component用于标注接口,是依赖需求方和依赖提供方之间的桥梁。被Component标注的接口在编译时会生成该接口的实现类(如果@Component标注的接口为CarComponent,则编译期生成的实现类为DaggerCarComponent),我们通过调用这个实现类的方法完成注入;
@Qulifier@Qulifier 用于自定义注解,也就是说 @Qulifier 就如同Java提供的几种基本元注解一样用来标记注解类。我们在使用 @Module 来标注提供依赖的方法时,方法名我们是可以随便定义的(虽然我们定义方法名一般以provide开头,但这并不是强制的,只是为了增加可读性而已)。那么 Dagger2 怎么知道这个方法是为谁提供依赖呢?答案就是返回值的类型,Dagger2 根据返回值的类型来决定为哪个被 @Inject 标记了的变量赋值。但是问题来了,一旦有多个一样的返回类型Dagger2就懵逼了。@Qulifier 的存在正式为了解决这个问题,我们使用 @Qulifier 来定义自己的注解,然后通过自定义的注解去标注提供依赖的方法和依赖需求方(也就是被 @Inject 标注的变量),这样 Dagger2 就知道为谁提供依赖了。
@Scope@Scope同样用于自定义注解,我能可以通过@Scope自定义的注解来限定注解作用域,实现局部的单例;
@Singleton@Singleton其实就是一个通过@Scope定义的注解,我们一般通过它来实现全局单例。但实际上它并不能提前全局单例,是否能提供全局单例还要取决于对应的Component是否为一个全局对象。

我们提到 @Inject 和 @Module 都可以提供依赖,那如果我们即在构造函数上通过标记 @Inject 提供依赖,有通过 @Module 提供依赖 Dagger2 会如何选择呢?具体规则如下:

  • 步骤1:首先查找 @Module 标注的类中是否存在提供依赖的方法。
  • 步骤2:若存在提供依赖的方法,查看该方法是否存在参数。
  • a:若存在参数,则按从步骤1开始依次初始化每个参数;
  • b:若不存在,则直接初始化该类实例,完成一次依赖注入。
  • 步骤3:若不存在提供依赖的方法,则查找 @Inject 标注的构造函数,看构造函数是否存在参数。
  • a:若存在参数,则从步骤 1 开始依次初始化每一个参数
  • b:若不存在,则直接初始化该类实例,完成一次依赖注入。

1. @Inject 的使用

在依赖项构成函数处声明 @Inject

Code
public class BrakingSystem {     /**     * 为被 @Inject 标记了的变量提供依赖     */    @Inject    public BrakingSystem() {    }     public void print() {        Log.i("", "------ 刹车系统: 碟刹");    }}

用@Component 标注的接口 BicycleComponent,这个 BicycleComponent 其实就是一个注入器,是连接依赖方与依赖项的桥梁

Code
@Componentpublic interface BicycleComponent {    void inject(Bicycle bicycle);}

在依赖方 Bicycle 中,

Code
public class Bicycle {     /**     * @Inject 标记需要依赖项的变量,告诉 Dagger2 为此变量创建对象并依赖     */    @Inject    public BrakingSystem brakingSystem;     public Bicycle() {        // 告诉了注入器 DaggerBicycleComponent 把依赖注入到了 Bicycle 类中,DaggerBicycleComponent类通过 apt 生成,需要项目 build(编译后)引用        DaggerBicycleComponent.builder().build().inject(this);    }}

2. @Module、@Provide 的使用

当依赖项的构造函数是带参数时,或者依赖项是三方类是我们无法修改时?就需要 @Module 和 @Provide 的方式了。

Code
public class Frame {     private String material;     public Frame(String material) {        this.material = material;    }     public void print() {        Log.i("Frame", "------ 车架: " + material);    }}

使用 Module 类来生成依赖对象,@Provide 则是用来标注具体提供依赖对象的方法

Code
@Modulepublic class BicycleModule {     public BicycleModule() {    }     /**     * @Provides 用于标注 Module 所标注的类中的方法,该方法在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了 @Inject 的变量赋值     */    @Provides    public Frame provideFrame() {        return new Frame("铝合金");    }}

同时对 Component 类进行修改,我们需要加上modules = {MarkCarModule.class},用来告诉Dagger2提供依赖的是MarkCarModule这个类。

Code
@Component(modules = BicycleModule.class)public interface BicycleComponent {    void inject(Bicycle bicycle);}

依赖方 Bicycle 中,告诉注入器 DaggerBicycleComponent 把 BicycleModule 提供的依赖注入到了 Bicycle 类中

Code
public class Bicycle {     @Inject    public Frame frame;     public Bicycle() {        // 告诉注入器 DaggerBicycleComponent 把 BicycleModule 提供的依赖注入到了 Bicycle 类中        DaggerBicycleComponent.builder().bicycleModule(new BicycleModule()).build().inject(this);    } 

3. @Name 的使用

@Named 注解在 Dagger 2 中区分不同实例的依赖项,从而实现更灵活的依赖注入。

Code
public class Bicycle {    /**     * @Named 注解在 Dagger 2 中区分不同实例的依赖项,从而实现更灵活的依赖注入。     */    @Inject    @Named("Air Fork")    public IFork fork;     public Bicycle() {        // 告诉了注入器 DaggerBicycleComponent 把 BicycleModule 提供的依赖注入到了 Bicycle 类中        DaggerBicycleComponent.builder().bicycleModule(new BicycleModule()).build().inject(this);    }}

4. @Qualifier 的使用

@Qualifier 注解用于标识特定的依赖项,以便在存在多个相同类型但不同实现的依赖项时,能够明确指定要注入的是哪一个。它通常与@Inject 和 @Provides 注解一起使用。

Code
public class Shifter {		// 用 Qualifier 解决依赖项歧义    @Qualifier    @Retention(RetentionPolicy.RUNTIME)    public @interface QualifierFrontShifter { }     @Qualifier    @Retention(RetentionPolicy.RUNTIME)    public @interface QualifierBackShifter { }}    public class Bicycle {     @Shifter.QualifierFrontShifter    @Inject    public Shifter frontShifter;     @Shifter.QualifierBackShifter    @Inject    public Shifter backShifter;     public Bicycle() {        // 告诉了注入器 DaggerBicycleComponent 把 BicycleModule 提供的依赖注入到了 Bicycle 类中        DaggerBicycleComponent.builder().bicycleModule(new BicycleModule()).build().inject(this);    }}

5. @Scope 的使用

作用域(Scope)是一种管理依赖对象的生命周期的机制。通过使用作用域,您可以指定依赖对象在应用程序中的生命周期范围,并确保在该范围内只创建一个实例。 作用域的使用可以帮助您避免在应用程序中创建多个不必要的实例,从而提高性能并确保对象的一致性。 Dagger 2 默认提供了一个常见的作用域:@Singleton。但您也可以创建自定义的作用域来满足特定需求。

Code
public class GearSystem {     /**     * 1. @Scope 定义一个 BicycleScope 注解     */    @Scope    @Retention(RetentionPolicy.RUNTIME)    public @interface BicycleScope {    }}  @Modulepublic class BicycleModule {     /**     * 2. @BicycleScope 去标记依赖提供方 BicycleModule     */    @GearSystem.BicycleScope    @Provides    GearSystem provideGearSystem(){        return new GearSystem("禧玛诺");    }}  /** * 3. @Scope 去标注注入器 */@GearSystem.BicycleScope@Component(modules = BicycleModule.class)public interface BicycleComponent {    void inject(Bicycle bicycle);}
!icon 1. Dagger2 原理就是用 apt 编译生成 DaggerBicycleComponent 中间类,帮我们实现依赖注入,看看生成的代码一目了然。
  1. Android Hilt 基于 Dagger2,看懂了 Dagger 2,也就懂了 Hilt。
  2. Koin 原理是运行时基于反射实现依赖注入,性能较差,也更简单(可参考 注解和反射实现 ButterKnifekoin 使用示例)。

因为依赖注入在大型工程里面会影响打包速度,虽听起来高大上,实际上多用、乱用很容易造成代码阅读困难。我在自己的项目里面是很少使用的,懂得原理,公司项目别人使用到也能很好理解做相应的维护修改。

参考文档