Dagger2 再学习

作者: DaZenD | 来源:发表于2021-04-27 08:08 被阅读0次

本文为dagger2的学习小记,有错请指正

dagger2 基本注解类

Dagger2 的注解主要有以下七类:

@Inject :

这个注解有两个作用:在目标类中标记成员变量告诉 Dagger 这个类型
的变量需要一个实例对象;标记依赖类中的构造方法,告诉 Dagger 我可以提供这种类型的依赖实例。

@Component :

用来标记接口或者抽象类,也被称为注入器,是 @Inject 和 @Module
的桥梁,所有的 Component 都可以通过它的 modules 知道它所提供的依赖范围,一个 Componet 可以依赖一个或多个 Component ,并拿到被依赖 Component 暴露出来的实例, Componenet 的 dependencies 属性就是确定依赖关系的实现。

@Module :

用来标记类,一般类名以 Module 结尾, Module 的主要作用是用来集中管理 @Provides 标记的方法,我们定义一个被 @Module 注解的类, Dagger 就会知道在哪里找到依赖来满足创建类的实例, Module 的一个重要特征是被设计成区块并可以组合在一起。

@Provides :

对方法进行注解,并且这些方法都是有返回类型的,告诉 Dagger 我
们向如何创建并提供该类型的依赖实例(一般会在方法中 new 出实例),用@Provides 标记的方法,推荐用 provide 作为前缀。

@Qualifier :

限定符,当一个类的类型不足以标示一个依赖的时候,我们就可以
用这个注解,它会调用 DataModule 中方法来返回合适的依赖类实例。

@Scope :

通过自定义注解来限定作用域,所有的对象都不再需要知道怎么管理它的
实例, Dagger2 中有一个默认的作用域注解 @Singleton ,通常用来标记在 App 整个生命周期内存活的实例,也可以定义一个@PerActivity 注解,用来表明生命周期要与 Activity 一致。

@SubComponent :

如果我们需要父组件全部的提供对象,我们就可以用包含方式,而不是用依赖方式,包含方式不需要父组件显示显露对象,就可以拿到父组件全部
对象,且 SubComponent 只需要在父 Component 接扣中声明就可以了。

@Inject 和 @Component

第一步:

依赖框架

第二步:

User 作为目标类中需要实例化的成员对象,给其构造函数添加@Inject标签:

    public class User {
        public String name;

        @Inject
        public User() {
            name = "lizejun";
        }

        public String getName() {
            return name;
        }
    }

第三步:声明 Component :

@Component()
public interface OnlyInjectComponent {
    void inject(AnnotationActivity annotationActivity);
}

第四步:在目标类中添加注解 @Inject ,并根据我们第 3 步中声明的 Component ,调用 DaggerXXX 方法来进行注入:

public class AnnotationActivity extends AppCompatActivity {

    @Inject
    public User mUser;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dragger2);
        // 在第 3 步声明的 Component 接口或者抽象类的基础上,添加 Dagger 前缀。
        DaggerOnlyInjectComponent.builder().build().inject(this);
    }
}

上面这个例子有两个缺点:

  • 只能标记一个构造方法,因为如果标记两个以上,不知道要用哪一个构造提供实例。
  • 不能标记其它我们不能修改的类,例如第三方库。
  • 如果用 @Inject 标记的构造函数如果有参数,那么这个参数也需要其它地方提供依赖,而类似于 String 这些我们不能修改的类,只能用 @Module 中的 @Provides来提供实例了。

采用 @Module 来提供依赖

采用 @Module 标记的类提供依赖是常规套路, @Module 标记的类起管理作用,真正提供依赖实例靠的是 @Provides 标记的带返回类型的方法。

第一步:

和上面类似,我们定义一个依赖类,但是它的构造方法并不需要用@Inject 标记:

public class Person {
    private String name;

    public Person() {
        this.name = "lizejun";
    }

    public String getName() {
        return name;
    }
}

第二步:需要定义一个 @Module 来管理这些依赖类的实例:

@Module
public class PersonDataModule {
    @Provides
    public Person providePerson() {
        return new Person();
    }
}

第三步:定义一个 @Component ,它指向上面定义的 @Module

@Component(modules = { PersonDataModule.class })
public interface PersonInj ectComponent
{
    void inject(PersonInjectActivity injectActivity);
}

第四步:在目标类中进行依赖注入

public class PersonInjectActivity extends Activity {
    @Inject
    Person mPerson;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DaggerPersonInjectComponent.create().inject(this);
        System.out.println("Person name=" + mPerson.getName());
    }
}

这里注入的方式有两种,一种是像上面这样的,它适合于PersonDataModule 中只有一个无参的构造方法,否则我们需要这样调用:

DaggerPersonInjectComponent.builder().personDataModule(new PersonDataModule()).build().inject(this);

初始化依赖实例的步骤

  • 查找 Module 中是否存在创建该类型的方法(即 @Component 标记的接口中包含了 @Module 标记的 Module 类,如果没有则直接查找@Inject 对应的构造方法)。

    • 如果存在创建类方法,则查看该方法是否有参数

      • 如果不存在参数,直接初始化该类的实例,一次依赖注入到此结束。
      • 如果存在参数,则从步骤 1 开始初始化每个参数。
    • 如果不存在创建类方法,则查找该类型的类中有 @Inject 标记的构造方法,查看构造方法是否有参数:

      • 如果不存在参数,则直接初始化该类实例,一次依赖注入到此结束。
      • 如果存在参数,则从步骤 1 开始初始化每个参数。

@Qualifier

在 Dagger 中,有一个已经定义好的限定符, @Name ,下面我们也自己定义一个限定符:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface PeopleThreeQualifier {
}

第一步:和前面类似,我们先定义一个需要实例化的依赖类:

public class People {
    private int count;

    public People() {
        count = 0;
    }

    public People(int count) {
    this.count = count;

    public int getCount() {
        return count;
    }
}

第二步:定义一个 DataModule

和前面不同的是,在它的provideXXX 方法的注解中,我们添加了@Name(xxx) 和自定义的注解 PeopleThreePeople :

@Modulepublic
class PeopleDataModule {
    @Provides
    @Named("Five People")
    People provideFivePeople() {
        return new People(5);
    }

    @Provides
    @Named("Ten People")
    People provideTenPeople() {
        return new People(10);
    }

    @Provides
    @PeopleThreeQualifier
    People provideThreePeople() {
        return new People(3);
    }
}

第三步:定义 Component

@Component(modules = PeopleDataModule.class)
public interface PeopleInjec tComponent
{
    void inject(PeopleInjectActivity peopleInjectActivity);
}

第四步:定义依赖的module

在目标类中进行依赖注入,在提供 @Inject 注解时,我们还需要声明和PeopleDataModule 中对应的限定符,这样 Dagger 就知道该用那个函数来生成目标类中的依赖类实例:

public class PeopleInjectActivity extends Activity {
    @Inject
    @Named("Five People")
    People mFivePeople;

    @Inject
    @Named("Ten People")
    People mTenPeople;

    @Inject
    @PeopleThreeQualifier
    People mThreePeople;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DaggerPeopleInjectComponent.builder().peopleDataModule(new PeopleDataModule()).build().inject(this);
    }
}

@Scope

@Scope 的作用主要是在组织 Component 和 Module 的时候起到一个提醒和管理的作用,在 Dagger 中,有一个默认的作用域@Singleton 。@Scope 的作用是: Dagger2 可以通过自定义Scope 注解,来限定通过 Module 和Inject 方式创建的类的实例的生命周期能够与目标类的生命周期相同。 Scope 的真正作用在与 Component 的组织:

  • 更好的管理 Component 之间的组织方式,不管是依赖方式还是包含方式,都有必要用自定的 Scope 注解标注这些 Component ,而且编译器会检查有依赖关系或包含关系的 Component ,若发现有Component 没有用自定义 Scope 注解,则会报错。

  • 更好地管理 Component 与 Module 之间地关系,编译器会检查 Component 管理的Module ,若发现 Component 的自定义 Scope 注解与 Module 中的标注创建类实例方法的注解不一样,就会报错。

  • 提高程序的可读性。

下面是一个使用 @Singleton 的例子:

第一步:定义需要实例化的类:

public class AnSingleObject {
    private String objectId;

    public AnSingleObject() {
        objectId = toString();
    }

    public String getObjectId() {
        return objectId;
    }
}

第二步:定义 DataModule

在它的 provideXXX 方法,提供了 @Singletion 注解:

@Module
public class AnSingleObjectDataModule {
    @Provides
    @Singleton
    AnSingleObject provideAnSingleObject() {
        return new AnSingleObject();
    }
}

第三步:定义 Component

和前面不同的是,需要给这个 Component 添加 @Singleton注解:

@Component(modules = { AnSingleObjectDataModule.class })
@Singleton
public abstract class AnSingleObjectInjectComponent {
    private static AnSingleObjectInjectComponent sInstance;

    public abstract void inject(AnSingleObjectInjectActivity anSingleObjectInjectActivity);

    public static AnSingleObjectInjectComponent getInstance() {
        if (sInstance == null) {
            sInstance = DaggerAnSingleObjectInjectComponent.create();
        }
        return sInstance;
    }
}

第四步:在目标类中进行依赖注入

每次启动 Activity 的时候,我们可以发现打印出来的 hash 值都是相同的:

public class AnSingleObjectInjectActivity extends Activity {
    @Inject
    AnSingleObject object;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        AnSingleObjectInjectComponent.getInstance().inject(this);
        System.out.println("AnSingleObject id=" + object.getObjectId());
    }
}

Component

Component 有三种组织方式:

  • 依赖:一个 Component 依赖一个或多个 Component ,采用的是@Component 的dependencies 属性。
  • 包含:这里就用到了 @SubComponent 注解,用它来标记接口或者抽象类,表示它可以被包干。一个 Component 可以包含一个或多个Component ,而且被包含的Component 还可以继续包含其它的Component 。
  • 继承:用一个 Component 继承另外一个 Component 。

问题

Unused Modules and Component Dependencies
https://dagger.dev/unused-modules

当Dagger处理器生成组件时,它只需要模块和组件依赖项的实例,这些实例是为绑定提供请求所显式需要的。

如果组件中使用的所有模块方法都是静态的,Dagger根本不需要该模块的实例。Dagger可以在不使用模块的情况下直接调用静态方法。
如果模块没有为组件提供绑定,则不需要该模块的实例来构造图。

模块:

provider方法不是静态的

@Module
public class ModuleA{
    @Provides
    @IntoSet
    String provideOneString(LocationManager depA, Context depB) {
        return "ABC";
    }
}

组件:MainActivityComponent

@Component(dependencies = ApplicationComponent.class,modules ={ModuleB.class,ModuleA.class})
public interface MainActivityComponent {
    void inject(MainActivity mainActivity);
}
dagger2编译的:DaggerMainActivityComponent - Builder()

分析:moduleB的provider方法都是静态的,所以dagger不需要该模块的实例,所以,DaggerMainActivityComponent build的时候不需要moduleB实例用于调用ModuleB_ProvideSomeStringsFactory(moduleB方法的工厂类)去提供对象

    @Deprecated
    public Builder moduleB(ModuleB moduleB) {
      Preconditions.checkNotNull(moduleB);
      return this;
    }

分析:moduleA就不一样,moduleA中有非静态的方法,所以,需要DaggerMainActivityComponent初始化时候传入moduleA实例,因为DaggerMainActivityComponent 让 moduleA的非静态方法的工厂类提供对象时候用到该moduleA实例

    public Builder moduleA(ModuleA moduleA) {
      this.moduleA = Preconditions.checkNotNull(moduleA);
      return this;
    }
    
    public Builder applicationComponent(ApplicationComponent applicationComponent) {
      this.applicationComponent = Preconditions.checkNotNull(applicationComponent);
      return this;
    }

总结:

依赖方,组件,模块,工厂类:

1:组件连接组装component给依赖方:
component.builder - 传入module实例 - build:initialize :module工厂类用module制作provider:factory对象{moudle, module获取application}

2:inject 注入给依赖方
DaggerApplicationComponent:inject - Application_MembersInjector(application, 各个provider.get factory生产的对象(其实就是去module里取))

3:如果module中的静态方法,component-module:去获取对象时候是不需要module实例的,所以,component在依赖方中初始化就不用传module实例,所以,上面的@Deprecated public Builder moduleB,因为moduleB是静态方法,用不到module实例,所以,初始化component时候也不用设置moduleB的实例了

相关文章

网友评论

    本文标题:Dagger2 再学习

    本文链接:https://www.haomeiwen.com/subject/prtyrltx.html