Dagger2 再学习

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

dagger2 基本注解类

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

@Inject :

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

@Component :

用来标记接口或者抽象类,也被称为注入器,是 @Inject 和 @Module
的桥梁,所有的 Component 都可以通过它的 modules 知道它所提供的依赖范围,一个 Component 可以依赖一个或多个 Component ,并拿到被依赖 Component 暴露出来的实例, Component 的 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的实例了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容